aboutsummaryrefslogtreecommitdiffstats
path: root/include/gamp/gamp.hpp
blob: 5cd7ab531a8d1179899d865c296aecdaec208c8a (plain)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
 * Author: Sven Gothel <sgothel@jausoft.com>
 * Copyright (c) 2022 Gothel Software e.K.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef GAMP_HPP_
#define GAMP_HPP_

#include <cinttypes>
#include <cmath>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <functional>
#include <limits>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include <iostream>
#include <cctype>

#include <jau/environment.hpp>
#include <jau/os/os_support.hpp>
#include <jau/float_math.hpp>

#include <jau/math/vec2f.hpp>
#include <jau/math/vec3f.hpp>
#include <jau/math/vec4f.hpp>
#include <jau/math/mat4f.hpp>
#include <jau/math/recti.hpp>

#include <jau/math/util/pmvmat4f.hpp>

#if defined(__EMSCRIPTEN__)
    #include <emscripten.h>
#else
    #define EMSCRIPTEN_KEEPALIVE
#endif

/**
 * Basic computer graphics math and utilities helping with the framebuffer and I/O tooling.
 */
namespace gamp {
    /** Width of the window, coordinate in window units. */
    extern int win_width;
    /** Height of the window, coordinate in window units. */
    extern int win_height;
    /** Ratio pixel-size / window-size, a DPI derivative per axis*/
    extern float devicePixelRatio[2];
    
    /**
     * Width of the framebuffer coordinate in pixels.
     *
     * Framebuffer origin 0/0 is top-left corner.
     */
    extern jau::math::Recti viewport;
    
    typedef std::vector<uint32_t> pixel_buffer_t; // 32-bit pixel
    extern pixel_buffer_t fb_pixels;
    /** Display frames per seconds */
    extern int display_frames_per_sec;
    /** Optional custom forced frames per seconds, pass to swap_gpu_buffer() by default. Defaults to -1, i.e. automatic fps. */
    extern int forced_fps;
    extern int font_height;

    //
    // gfx toolkit dependent API
    //

    /** GFX Toolkit: Initialize a window of given size with a usable framebuffer. */
    bool init_gfx_subsystem(const char* title, int window_width, int window_height, bool enable_vsync=true);
    /** GFX Toolkit: Swap GPU back to front framebuffer using given fps, maintaining vertical monitor synchronization if possible. fps <= 0 implies automatic fps. */
    void swap_gpu_buffer(int fps) noexcept;
    /** GFX Toolkit: Swap GPU back to front framebuffer using forced_fps, maintaining vertical monitor synchronization if possible. */
    inline void swap_gpu_buffer() noexcept { swap_gpu_buffer(forced_fps); }
    
    /** Returns frames per seconds, averaged over get_gpu_stat_period(). */
    float get_gpu_stats_fps() noexcept;
    /** Returns rendering costs per frame in seconds, averaged over get_gpu_stat_period(). */
    double get_gpu_stats_frame_costs() noexcept;
    /** Returns active sleeping period per frame in seconds, averaged over get_gpu_stat_period(). Only reasonable if swap_gpu_buffer() has been called with fps > 0. */
    double get_gpu_stats_frame_sleep() noexcept;
    /** Sets the period length to average get_gpu_fps(), get_gpu_frame_costs(), get_gpu_frame_sleep() statistics. Defaults to 5s.*/
    void set_gpu_stats_period(int64_t milliseconds) noexcept;
    /** Returns the current period length for statistics in milliseconds, see set_gpu_stat_period(). Defaults is 5s. */
    int64_t get_gpu_stats_period() noexcept;
    /** Print statistics on the console to stdout after get_gpu_stat_period(). */
    void set_gpu_stats_show(bool enable) noexcept;
    /** Returns whether statistics are printed on the console, see set_show_gpu_stats(). */
    bool get_gpu_stats_show() noexcept;
    
    //
    // input
    //

    /** Fixed input action enumerator, useful to denote typical game actions e.g. cursor keys. */
    enum class input_event_type_t : int {
        NONE,
        POINTER_BUTTON,
        POINTER_MOTION,
        ANY_KEY_UP,
        ANY_KEY_DOWN,
        P1_UP, // 5
        P1_DOWN,
        P1_RIGHT,
        P1_LEFT,
        P1_ACTION1,
        P1_ACTION2,
        P1_ACTION3,
        PAUSE, // 11
        P2_UP,
        P2_DOWN,
        P2_RIGHT,
        P2_LEFT,
        P2_ACTION1,
        P2_ACTION2,
        P2_ACTION3,
        RESET, // 18
        /** Request to close window, which then should be closed by caller */
        WINDOW_CLOSE_REQ,
        WINDOW_RESIZED, // 20
    };
    constexpr int bitno(const input_event_type_t e) noexcept {
        return static_cast<int>(e) - static_cast<int>(input_event_type_t::P1_UP);
    }
    constexpr uint32_t bitmask(const input_event_type_t e) noexcept {
        return 1U << bitno(e);
    }
    constexpr uint32_t bitmask(const int bit) noexcept {
        return 1U << bit;
    }

    inline bool is_ascii_code(int c) noexcept {
        return 0 != std::iscntrl(c) || 0 != std::isprint(c);
    }

    class input_event_t {
        private:
            constexpr static const uint32_t p1_mask =
                    bitmask(input_event_type_t::P1_UP) |
                    bitmask(input_event_type_t::P1_DOWN) |
                    bitmask(input_event_type_t::P1_RIGHT) |
                    bitmask(input_event_type_t::P1_LEFT) |
                    bitmask(input_event_type_t::P1_ACTION1) |
                    bitmask(input_event_type_t::P1_ACTION2) |
                    bitmask(input_event_type_t::P1_ACTION3);

            constexpr static const uint32_t p2_mask =
                    bitmask(input_event_type_t::P2_UP) |
                    bitmask(input_event_type_t::P2_DOWN) |
                    bitmask(input_event_type_t::P2_RIGHT) |
                    bitmask(input_event_type_t::P2_LEFT) |
                    bitmask(input_event_type_t::P2_ACTION1) |
                    bitmask(input_event_type_t::P2_ACTION2) |
                    bitmask(input_event_type_t::P2_ACTION3);
            uint32_t m_pressed; // [P1_UP..RESET]
            uint32_t m_lifted; // [P1_UP..RESET]
            bool m_paused;
        public:
            input_event_type_t last;
            /** ASCII code, ANY_KEY_UP, ANY_KEY_DOWN key code */
            uint16_t last_key_code;
            std::string text;
            int pointer_id;
            int pointer_x;
            int pointer_y;

            input_event_t() noexcept { clear(); }
            void clear() noexcept {
                m_pressed = 0;
                m_lifted = 0;
                m_paused = false;
                last = input_event_type_t::NONE;
                pointer_id = -1;
                pointer_x = -1;
                pointer_y = -1;
            }
            void pointer_motion(int id, int x, int y) noexcept {
                set(input_event_type_t::POINTER_MOTION);
                pointer_id = id;
                pointer_x = x;
                pointer_y = y;
            }
            void set(input_event_type_t e, uint16_t key_code=0) noexcept {
                const int bit = bitno(e);
                if( 0 <= bit && bit <= 31 ) {
                    const uint32_t m = bitmask(bit);
                    m_lifted &= ~m;
                    m_pressed |= m;
                }
                this->last = e;
                this->last_key_code = key_code;
                if( this->text.length() > 0 && '\n' == this->text[this->text.length()-1] ) {
                    this->text.clear();
                }
                if( 0 != key_code && is_ascii_code(key_code) ) {
                    if( 0x08 == key_code ) {
                        if( this->text.length() > 0 ) {
                            this->text.pop_back();
                        }
                    } else {
                        this->text.push_back( (char)key_code );
                    }
                }
            }
            void clear(input_event_type_t e, uint16_t key_code=0) noexcept {
                (void)key_code;
                const int bit = bitno(e);
                if( 0 <= bit && bit <= 31 ) {
                    const uint32_t m = bitmask(bit);
                    m_lifted |= m_pressed & m;
                    m_pressed &= ~m;
                    this->last_key_code = 0;
                }
                if( input_event_type_t::PAUSE == e ) {
                    m_paused = !m_paused;
                }
            }
            bool paused() const noexcept { return m_paused; }
            bool pressed(input_event_type_t e) const noexcept {
                const int bit = bitno(e);
                if( 0 <= bit && bit <= 31 ) {
                    return 0 != ( m_pressed & bitmask(bit) );
                } else {
                    return false;
                }
            }
            bool pressed_and_clr(input_event_type_t e) noexcept {
                if( pressed(e) ) {
                    clear(e);
                    return true;
                } else {
                    return false;
                }
            }
            bool released_and_clr(input_event_type_t e) noexcept {
                const int bit = bitno(e);
                if( 0 <= bit && bit <= 31 ) {
                    const uint32_t m = bitmask(bit);
                    if( 0 != ( m_lifted & m ) ) {
                        m_lifted &= ~m;
                        return true;
                    }
                }
                return false;
            }
            bool has_any_p1() const noexcept {
                return 0 != ( ( m_pressed | m_lifted ) & p1_mask );
            }
            bool has_any_p2() const noexcept {
                return 0 != ( ( m_pressed | m_lifted ) & p2_mask );
            }
            std::string to_string() const noexcept;
    };
    inline std::string to_string(const input_event_t& e) noexcept { return e.to_string(); }

    /**
     * GFX Toolkit: Handle windowing and keyboard events.
     *
     * Should be called until function returns false
     * to process all buffered events.
     *
     * @param event
     * @return true if event received, false otherwise
     */
    bool handle_one_event(input_event_t& event) noexcept;

    inline bool handle_events(input_event_t& event) noexcept {
        bool one = false;
        while( gamp::handle_one_event(event) ) {
            one = true;
            // std::cout << "Input " << to_string(event) << std::endl;
        }
        return one;
    }    
}

#endif /*  GAMP_HPP_ */