resonance 0.1.0

A modular game engine. Heavy work in progress.
Documentation

use crate::window::{Window, WindowConfig, WindowEvent};
use crate::app::Engine;
use crate::input::Input;
use crate::renderer::Renderer;
use std::sync::Arc;
use std::time::{Duration, Instant};
use winit::{
    application::ApplicationHandler,
    event::{DeviceEvent, DeviceId, ElementState, StartCause, WindowEvent as WinitWindowEvent},
    event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
    window::WindowId,
};

pub struct WindowApp {
    engine: Option<Engine>,
    window_config: WindowConfig,
    last_update: Option<Instant>,
    target_frame_time: Duration,
    should_update: bool,
}

impl WindowApp {
    pub fn new(engine: Engine, window_config: WindowConfig) -> Self {
        Self {
            engine: Some(engine),
            window_config,
            last_update: None,
            target_frame_time: Duration::from_micros(1_000_000 / 60),
            should_update: false,
        }
    }

    fn update_engine(&mut self) {
        let now = Instant::now();

        if let Some(last) = self.last_update {
            let elapsed = now.duration_since(last);
            if elapsed < self.target_frame_time {
                return;
            }
        }

        if let Some(ref mut engine) = self.engine {
            if engine.is_running() {
                engine.update();

                if let Some(mut input) = engine.world.get_resource_mut::<Input>() {
                    input.update();
                }
            }
        }

        self.last_update = Some(now);
    }
}

impl ApplicationHandler for WindowApp {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {

        if let Some(ref mut engine) = self.engine {
            if !engine.world.contains_resource::<Window>() {

                let window = match Window::new(event_loop, &self.window_config) {
                    Ok(w) => w,
                    Err(e) => {
                        log::error!("Failed to create window: {}", e);
                        event_loop.exit();
                        return;
                    }
                };

                log::info!("Window created and added to engine");

                let renderer = match crate::renderer::create_renderer_sync(Arc::clone(&window.window)) {
                    Ok(r) => r,
                    Err(e) => {
                        log::error!("Failed to create renderer: {}", e);
                        event_loop.exit();
                        return;
                    }
                };
                log::info!("Renderer created and added to engine");

                engine.world.insert_resource(window);
                engine.world.insert_resource(renderer);

                engine.startup();
            }
        }
    }

    fn window_event(
        &mut self,
        event_loop: &ActiveEventLoop,
        _window_id: WindowId,
        event: WinitWindowEvent,
    ) {
        match event {
            WinitWindowEvent::CloseRequested => {
                log::info!("Window close requested");
                if let Some(ref mut engine) = self.engine {
                    engine.stop();
                }
                event_loop.exit();
            }
            WinitWindowEvent::Resized(size) => {
                log::debug!("Window resized: {}x{}", size.width, size.height);
                if let Some(ref mut engine) = self.engine {

                    if let Some(mut renderer) = engine.world.get_resource_mut::<Renderer>() {
                        renderer.resize(size.width, size.height);
                    }

                    engine.world.write_message(WindowEvent::Resized {
                        width: size.width,
                        height: size.height,
                    });
                }
            }
            WinitWindowEvent::Focused(focused) => {
                log::debug!("Window focus changed: {}", focused);
                if let Some(ref mut engine) = self.engine {
                    engine.world.write_message(WindowEvent::Focused(focused));
                }
            }
            WinitWindowEvent::KeyboardInput { event, .. } => {

                if let Some(ref mut engine) = self.engine {
                    if let Some(mut input) = engine.world.get_resource_mut::<Input>() {

                        if let winit::keyboard::PhysicalKey::Code(key_code) = event.physical_key {
                            match event.state {
                                ElementState::Pressed => {
                                    input.keyboard.press(key_code);
                                }
                                ElementState::Released => {
                                    input.keyboard.release(key_code);
                                }
                            }
                        }
                    }
                }
            }
            WinitWindowEvent::CursorMoved { position, .. } => {

                if let Some(ref mut engine) = self.engine {
                    if let Some(mut input) = engine.world.get_resource_mut::<Input>() {
                        input.mouse.update_position(position.x as f32, position.y as f32);
                    }
                }
            }
            WinitWindowEvent::MouseInput { state, button, .. } => {

                if let Some(ref mut engine) = self.engine {
                    if let Some(mut input) = engine.world.get_resource_mut::<Input>() {
                        match state {
                            ElementState::Pressed => {
                                input.mouse.press_button(button);
                            }
                            ElementState::Released => {
                                input.mouse.release_button(button);
                            }
                        }
                    }
                }
            }
            WinitWindowEvent::MouseWheel { delta, .. } => {

                if let Some(ref mut engine) = self.engine {
                    if let Some(mut input) = engine.world.get_resource_mut::<Input>() {
                        match delta {
                            winit::event::MouseScrollDelta::LineDelta(_, y) => {
                                input.mouse.scroll(y * 10.0);
                            }
                            winit::event::MouseScrollDelta::PixelDelta(pos) => {
                                input.mouse.scroll(pos.y as f32);
                            }
                        }
                    }
                }
            }
            WinitWindowEvent::RedrawRequested => {

                self.should_update = true;
            }
            _ => {}
        }
    }

    fn device_event(
        &mut self,
        _event_loop: &ActiveEventLoop,
        _device_id: DeviceId,
        event: DeviceEvent,
    ) {
        match event {
            DeviceEvent::MouseMotion { delta } => {

                if let Some(ref mut engine) = self.engine {
                    if let Some(mut input) = engine.world.get_resource_mut::<Input>() {
                        input.mouse.add_motion_delta(delta.0 as f32, delta.1 as f32);
                    }
                }
            }
            _ => {}
        }
    }

    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {

        self.update_engine();

        if let Some(ref engine) = self.engine {
            if let Some(window) = engine.world.get_resource::<Window>() {
                window.window.request_redraw();
            }

            if !engine.is_running() {
                event_loop.exit();
            }
        }
    }

    fn new_events(&mut self, event_loop: &ActiveEventLoop, _cause: StartCause) {

        event_loop.set_control_flow(ControlFlow::Poll);
    }
}

pub fn run(engine: Engine) {

    let config = if let Some(config) = engine.world.get_resource::<WindowConfig>() {
        config.clone()
    } else {
        log::info!("No WindowConfig found, using default configuration");
        WindowConfig::default()
    };

    let event_loop = EventLoop::new()
        .expect("Failed to create event loop");
    event_loop.set_control_flow(ControlFlow::Poll);

    log::info!("Starting window event loop");

    let mut app = WindowApp::new(engine, config);

    event_loop.run_app(&mut app)
        .expect("Failed to run event loop");

    log::info!("Window event loop exited");
}