summaryrefslogtreecommitdiff
path: root/engine/src/lib.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-04-14 12:34:52 +0200
committerHampusM <hampus@hampusmat.com>2024-04-14 12:35:28 +0200
commit101b455e51f9b702da5517cabe2c3b1086fcb2e7 (patch)
tree470e28acd7a3777dbb4be0208f9cd3177bba52a9 /engine/src/lib.rs
parentef7b76ff39d501028852835649f618fcbe17a003 (diff)
feat(engine): use ECS architecture
Diffstat (limited to 'engine/src/lib.rs')
-rw-r--r--engine/src/lib.rs369
1 files changed, 63 insertions, 306 deletions
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index 332a011..f83d85b 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -1,367 +1,124 @@
#![deny(clippy::all, clippy::pedantic)]
-
-use std::collections::BTreeMap;
-use std::time::{Duration, Instant};
-
-use glfw::window::KeyState;
-use glfw::{Window, WindowBuilder};
-
-use crate::camera::Camera;
-use crate::lighting::LightSource;
-use crate::object::{Id as ObjectId, Object};
-use crate::renderer::Renderer;
-use crate::vector::Vec2;
+#![allow(clippy::needless_pass_by_value)]
+
+use ecs::component::Sequence as ComponentSequence;
+use ecs::event::Event;
+use ecs::extension::Extension;
+use ecs::sole::Sole;
+use ecs::system::{Into, System};
+use ecs::{SoleAlreadyExistsError, World};
+
+use crate::delta_time::{update as update_delta_time, DeltaTime, LastUpdate};
+use crate::event::{
+ PostPresent as PostPresentEvent,
+ PreUpdate as PreUpdateEvent,
+ Present as PresentEvent,
+ Start as StartEvent,
+ Update as UpdateEvent,
+};
mod opengl;
mod projection;
-mod renderer;
mod shader_preprocessor;
-mod transform;
pub mod camera;
pub mod data_types;
+pub mod delta_time;
+pub mod event;
+pub mod input;
pub mod lighting;
pub mod material;
pub mod math;
pub mod mesh;
-pub mod object;
+pub mod renderer;
pub mod shader;
pub mod texture;
+pub mod transform;
pub mod vertex;
+pub mod window;
-pub use glfw::window::Key;
-pub use glfw::WindowSize;
+pub extern crate ecs;
pub(crate) use crate::data_types::matrix;
pub use crate::data_types::{color, vector};
+type EventOrder = (PreUpdateEvent, UpdateEvent, PresentEvent, PostPresentEvent);
+
#[derive(Debug)]
-pub struct Engine<CameraT>
+pub struct Engine
{
- /// Objects have to be dropped before window. Otherwise, UB.
- objects: BTreeMap<ObjectId, Object>,
- light_source: Option<LightSource>,
- renderer: Renderer<CameraT>,
- window: Window,
- delta_time: Duration,
+ world: World,
}
-impl<CameraT> Engine<CameraT>
-where
- CameraT: Camera,
+impl Engine
{
/// Creates and initializes a new engine.
///
/// # Errors
/// Will return `Err` if window creation or window configuration fails.
- pub fn new(window_settings: &WindowSettings, camera: CameraT) -> Result<Self, Error>
- {
- 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_settings.size, &window_settings.title)
- .map_err(Error::CreateWindowFailed)?;
-
- window
- .make_context_current()
- .map_err(Error::ConfigureWindowFailed)?;
-
- window.set_framebuffer_size_callback(move |new_window_size| {
- Renderer::<CameraT>::set_viewport(&Vec2::ZERO, &new_window_size);
- });
-
- Self::set_window_input_modes(&window, window_settings)?;
-
- window
- .set_cursor_mode(window_settings.cursor_mode)
- .map_err(|err| Error::SetWindowCursorModeFailed {
- source: err,
- cursor_mode: window_settings.cursor_mode,
- })?;
-
- let renderer =
- Renderer::new(&window, camera).map_err(Error::InitializeRendererFailed)?;
-
- Ok(Self {
- window,
- renderer,
- objects: BTreeMap::new(),
- light_source: None,
- delta_time: Duration::ZERO,
- })
- }
-
- pub fn add_object(&mut self, object: Object)
- {
- self.objects.insert(object.id(), object);
- }
-
- pub fn set_objecs(&mut self, objects: impl IntoIterator<Item = Object>)
+ #[must_use]
+ pub fn new() -> Self
{
- self.objects
- .extend(objects.into_iter().map(|object| (object.id(), object)));
- }
+ let mut world = World::new();
- pub fn set_light_source(&mut self, light_source: LightSource)
- {
- self.light_source = Some(light_source);
- }
+ world.add_sole(DeltaTime::default()).ok();
- #[must_use]
- pub fn light_source(&self) -> Option<&LightSource>
- {
- self.light_source.as_ref()
- }
+ world.register_system(
+ PreUpdateEvent,
+ update_delta_time
+ .into_system()
+ .initialize((LastUpdate::default(),)),
+ );
- #[must_use]
- pub fn light_source_mut(&mut self) -> Option<&mut LightSource>
- {
- self.light_source.as_mut()
+ Self { world }
}
- #[must_use]
- pub fn get_object_by_id(&self, id: ObjectId) -> Option<&Object>
+ pub fn spawn<Comps>(&mut self, components: Comps)
+ where
+ Comps: ComponentSequence,
{
- self.objects.get(&id)
+ self.world.create_entity(components);
}
- #[must_use]
- pub fn get_object_by_id_mut(&mut self, id: ObjectId) -> Option<&mut Object>
+ pub fn register_system<'this, SystemImpl>(
+ &'this mut self,
+ event: impl Event,
+ system: impl System<'this, SystemImpl>,
+ )
{
- self.objects.get_mut(&id)
+ self.world.register_system(event, system);
}
- /// Starts the engine.
+ /// Adds a globally shared singleton value.
///
/// # Errors
- /// Will return `Err` if updating the window fails.
- pub fn start(&mut self, mut func: impl FnMut(&mut Self)) -> Result<(), Error>
- {
- let mut prev_frame_start: Option<Instant> = None;
-
- while !self.window.should_close() {
- self.update_delta_time(&mut prev_frame_start);
-
- func(self);
-
- let window_size = self.window.size().map_err(Error::GetWindowSizeFailed)?;
-
- self.renderer
- .render(
- self.objects.values(),
- self.light_source.as_ref(),
- &window_size,
- )
- .map_err(Error::RenderingFailed)?;
-
- self.window
- .swap_buffers()
- .map_err(Error::UpdateWindowFailed)?;
-
- self.window
- .poll_events()
- .map_err(Error::UpdateWindowFailed)?;
- }
-
- Ok(())
- }
-
- #[must_use]
- pub fn camera(&self) -> &CameraT
- {
- self.renderer.camera()
- }
-
- pub fn camera_mut(&mut self) -> &mut CameraT
- {
- self.renderer.camera_mut()
- }
-
- /// Returns the current delta time. Will be 0 if not called from the function
- /// passed to [`start`].
- #[must_use]
- pub fn delta_time(&self) -> &Duration
+ /// Returns `Err` if this [`Sole`] has already been added.
+ pub fn add_sole(&mut self, sole: impl Sole) -> Result<(), SoleAlreadyExistsError>
{
- &self.delta_time
+ self.world.add_sole(sole)
}
- /// Returns whether or not a keyboard key is currently pressed.
- ///
- /// # Errors
- /// Will return `Err` if getting the key state fails.
- pub fn is_key_pressed(&self, key: Key) -> Result<bool, Error>
+ pub fn add_extension(&mut self, extension: impl Extension)
{
- self.window
- .get_key(key)
- .map(|key_state| matches!(key_state, KeyState::Pressed))
- .map_err(Error::GetKeyStateFailed)
+ self.world.add_extension(extension);
}
- /// Returns the cursor position.
+ /// Starts the engine.
///
/// # Errors
- /// Will return `Err` if unable to get the cursor position from GLFW.
- pub fn get_cursor_pos(&self) -> Result<Vec2<f64>, Error>
- {
- let pos = self
- .window
- .get_cursor_position()
- .map_err(Error::GetCursorPosFailed)?;
-
- Ok(Vec2 { x: pos.x, y: pos.y })
- }
-
- fn update_delta_time(&mut self, prev_frame_start: &mut Option<Instant>)
- {
- let frame_start_time = Instant::now();
-
- if let Some(last_frame_start) = prev_frame_start {
- self.delta_time = frame_start_time.duration_since(*last_frame_start);
- }
-
- *prev_frame_start = Some(frame_start_time);
- }
-
- fn set_window_input_modes(
- window: &Window,
- window_settings: &WindowSettings,
- ) -> Result<(), Error>
- {
- for (input_mode, enabled) in &window_settings.input_modes {
- window
- .set_input_mode(*input_mode, *enabled)
- .map_err(|err| Error::SetWindowInputModeFailed {
- source: err,
- input_mode: *input_mode,
- enabled: *enabled,
- })?;
- }
-
- 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),
-
- #[error("Failed to get window size")]
- GetWindowSizeFailed(#[source] glfw::Error),
-
- #[error("Failed to set window input mode {input_mode:?} to {enabled}")]
- SetWindowInputModeFailed
- {
- #[source]
- source: glfw::Error,
- input_mode: InputMode,
- enabled: bool,
- },
-
- #[error("Failed to set window cursor mode to {cursor_mode:?}")]
- SetWindowCursorModeFailed
- {
- #[source]
- source: glfw::Error,
- cursor_mode: CursorMode,
- },
-
- #[error("Failed to get key state")]
- GetKeyStateFailed(#[source] glfw::Error),
-
- #[error("Failed to get cursor position")]
- GetCursorPosFailed(#[source] glfw::Error),
-
- #[error("Rendering failed")]
- RenderingFailed(#[source] renderer::Error),
-}
-
-#[derive(Debug)]
-pub struct WindowSettings
-{
- size: WindowSize,
- title: Box<str>,
- input_modes: BTreeMap<InputMode, bool>,
- cursor_mode: CursorMode,
-}
-
-#[derive(Debug)]
-pub struct WindowSettingsBuilder
-{
- input_modes: BTreeMap<InputMode, bool>,
- cursor_mode: CursorMode,
-}
-
-impl WindowSettingsBuilder
-{
- #[must_use]
- pub fn new() -> Self
- {
- Self::default()
- }
-
- #[must_use]
- pub fn input_mode(mut self, input_mode: InputMode, enabled: bool) -> Self
- {
- self.input_modes.insert(input_mode, enabled);
-
- self
- }
-
- #[must_use]
- pub fn input_modes(
- mut self,
- input_modes: impl IntoIterator<Item = (InputMode, bool)>,
- ) -> Self
- {
- self.input_modes.extend(input_modes);
-
- self
- }
-
- #[must_use]
- pub fn cursor_mode(mut self, cursor_mode: CursorMode) -> Self
+ /// Will return `Err` if updating the window fails.
+ pub fn start(&self)
{
- self.cursor_mode = cursor_mode;
-
- self
- }
+ self.world.emit(StartEvent);
- pub fn build(&self, size: WindowSize, title: impl Into<Box<str>>) -> WindowSettings
- {
- WindowSettings {
- size,
- title: title.into(),
- input_modes: self.input_modes.clone(),
- cursor_mode: self.cursor_mode,
- }
+ self.world.event_loop::<EventOrder>();
}
}
-impl Default for WindowSettingsBuilder
+impl Default for Engine
{
fn default() -> Self
{
- Self {
- input_modes: BTreeMap::new(),
- cursor_mode: CursorMode::Normal,
- }
+ Self::new()
}
}
-
-pub use glfw::window::{CursorMode, InputMode};