frapp/
lib.rs

1pub use assets_manager;
2pub use frenderer;
3use frenderer::clock::{Clock, Instant};
4use frenderer::input::Input;
5use frenderer::Frenderer;
6pub use frenderer::FrendererEvents;
7use frenderer::{Driver, EventPhase};
8pub use winit::{self, window::WindowBuilder};
9
10/// `frapp` exposes an alias for [assets_manager::AssetCache] that uses a different source depending on whether we're targeting native or web.
11#[cfg(not(target_arch = "wasm32"))]
12pub type AssetCache = assets_manager::AssetCache<assets_manager::source::FileSystem>;
13#[cfg(target_arch = "wasm32")]
14pub type AssetCache = assets_manager::AssetCache<assets_manager::source::Embedded<'static>>;
15
16/// App is the main public trait of `frapp`.  Implementors get a defined new/update/render lifecycle with a choice of frenderer renderers (either [frenderer::Renderer] or [frenderer::Immediate]).
17pub trait App {
18    /// Target delta-time for simulation
19    const DT: f32;
20    /// The renderer type to use
21    type Renderer: frenderer::Frenderer;
22    /// Initialize the app
23    fn new(renderer: &mut Self::Renderer, assets: AssetCache) -> Self;
24    /// Update (called every DT seconds)
25    fn update(&mut self, renderer: &mut Self::Renderer, input: &Input);
26    /// Render (called once per present cycle)
27    fn render(&mut self, renderer: &mut Self::Renderer, dt: f32, input: &Input);
28}
29
30use std::marker::PhantomData;
31
32/// AppDriver is public, but should only be created from the [app] macro.
33pub struct AppDriver<A: App + 'static>
34where
35    A::Renderer: From<frenderer::Renderer> + FrendererEvents<()>,
36{
37    cache: AssetCache,
38    _phantom: PhantomData<A>,
39}
40
41impl<A: App + 'static> AppDriver<A>
42where
43    A::Renderer: From<frenderer::Renderer> + FrendererEvents<()>,
44{
45    /// Calling [run] hands off control to `winit` and `frenderer`.
46    pub fn run(self, builder: winit::window::WindowBuilder, render_dims: Option<(u32, u32)>) {
47        let drv = Driver::new(builder, render_dims);
48        let mut clock = Clock::new(A::DT, 0.0002, 5);
49        let mut last_render = Instant::now();
50        drv.run_event_loop::<(), _>(
51            move |window, renderer| {
52                let input = Input::default();
53                let mut rend: A::Renderer = renderer.into();
54                let app = A::new(&mut rend, self.cache);
55                (window, app, rend, input)
56            },
57            move |event, target, (window, ref mut app, ref mut renderer, ref mut input)| {
58                match renderer.handle_event(&mut clock, window, &event, target, input) {
59                    EventPhase::Run(steps) => {
60                        for _ in 0..steps {
61                            app.update(renderer, input);
62                            input.next_frame();
63                        }
64                        app.render(renderer, last_render.elapsed().as_secs_f32(), input);
65                        last_render = Instant::now();
66                        renderer.render();
67                    }
68                    EventPhase::Quit => {
69                        target.exit();
70                    }
71                    EventPhase::Wait => {}
72                }
73            },
74        )
75        .unwrap()
76    }
77    /// New is public for its use in a macro, it isn't super helpful to call it directly.
78    pub fn new(cache: AssetCache) -> Self {
79        Self {
80            cache,
81            _phantom: std::marker::PhantomData,
82        }
83    }
84}
85/// `app!` takes an implementor of [App] and a path to a content folder and sets up an [AppDriver] on which [AppDriver::run] can be called to start the program.
86#[macro_export]
87macro_rules! app {
88    ($et:ty, $content:literal) => {{
89        use frapp::{assets_manager, AppDriver};
90        #[cfg(not(target_arch = "wasm32"))]
91        let source =
92            assets_manager::source::FileSystem::new($content).expect("Couldn't load resources");
93        #[cfg(target_arch = "wasm32")]
94        let source =
95            assets_manager::source::Embedded::from(assets_manager::source::embed!($content));
96        let cache = assets_manager::AssetCache::with_source(source);
97        AppDriver::<$et>::new(cache)
98    }};
99}