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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use super::*;

pub struct Geng {
    window: Window,
    #[cfg(feature = "audio")]
    pub(crate) audio: AudioContext,
    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,
            #[cfg(feature = "audio")]
            audio: AudioContext::new(),
            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
    }
}

fn run_impl(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 mut 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();

            !matches!(state.borrow_mut().transition(), Some(Transition::Pop))
        }
    };

    #[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(move || {
            main_loop();
        }) as Box<dyn FnMut()>);
        run(main_loop.as_ref());
        main_loop.forget();
    }

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

pub fn run(geng: Rc<Geng>, state: impl State) {
    let mut state_manager = StateManager::new();
    state_manager.push(Box::new(state));
    let state = DebugOverlay::new(&geng, state_manager);
    run_impl(geng, state);
}