1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use super::*;

pub struct Geng {
    window: Window,
    shader_lib: ShaderLib,
    draw_2d: Rc<Draw2D>,
    pub(crate) asset_manager: AssetManager,
    default_font: Rc<Font>,
    max_delta_time: Cell<f64>,
}

pub struct ContextOptions {
    pub title: String,
    pub vsync: bool,
    pub max_delta_time: f64,
}

impl Default for ContextOptions {
    fn default() -> Self {
        Self {
            title: "Geng Application".to_string(),
            vsync: true,
            max_delta_time: 0.1,
        }
    }
}

impl Geng {
    pub fn new(options: ContextOptions) -> Self {
        let window = Window::new(&options.title, options.vsync);
        let ugli = window.ugli().clone();
        let shader_lib = ShaderLib::new(window.ugli());
        let draw_2d = Rc::new(Draw2D::new(&shader_lib, &ugli));
        let default_font = Rc::new({
            let data = include_bytes!("font/default.ttf") as &[u8];
            Font::new_with(window.ugli(), &shader_lib, data.to_owned()).unwrap()
        });
        Geng {
            window,
            shader_lib,
            draw_2d,
            asset_manager: AssetManager::new(),
            default_font,
            max_delta_time: Cell::new(options.max_delta_time),
        }
    }

    pub fn window(&self) -> &Window {
        &self.window
    }

    pub fn ugli(&self) -> &Rc<Ugli> {
        self.window.ugli()
    }

    pub fn shader_lib(&self) -> &ShaderLib {
        &self.shader_lib
    }

    pub fn draw_2d(&self) -> &Rc<Draw2D> {
        &self.draw_2d
    }

    pub fn default_font(&self) -> &Rc<Font> {
        &self.default_font
    }
}

pub fn run(geng: Rc<Geng>, state: impl State) {
    let state = Rc::new(RefCell::new(state));
    geng.window.set_event_handler(Box::new({
        let state = state.clone();
        move |event| {
            state.borrow_mut().handle_event(event);
        }
    }));

    let mut timer = Timer::new();
    let main_loop = {
        let geng = geng.clone();
        move || {
            let delta_time = timer.tick();
            let delta_time = delta_time.min(geng.max_delta_time.get());
            state.borrow_mut().update(delta_time);

            let mut framebuffer = ugli::Framebuffer::default(geng.ugli());
            state.borrow_mut().draw(&mut framebuffer);

            geng.window.swap_buffers();
        }
    };

    #[cfg(target_arch = "wasm32")]
    {
        #[wasm_bindgen(inline_js = r#"
        export function run(main_loop) {
            function main_loop_wrapper() {
                main_loop();
                window.requestAnimationFrame(main_loop_wrapper);
            }
            main_loop_wrapper();
        }
        "#)]
        extern "C" {
            fn run(main_loop: &wasm_bindgen::JsValue);
        }
        let main_loop =
            wasm_bindgen::closure::Closure::wrap(Box::new(main_loop) as Box<dyn FnMut()>);
        run(main_loop.as_ref());
        main_loop.forget();
    }

    #[cfg(not(target_arch = "wasm32"))]
    {
        let mut main_loop = main_loop;
        while !geng.window.should_close() {
            main_loop();
        }
    }
}