init
[fishladder.git] / vg / vg.h
1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <dirent.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <stdarg.h>
9
10 #include "gl/glad/glad.h"
11 #include "gl/glfw3.h"
12
13 void vg_register_exit( void( *funcptr )(void), const char *name );
14 void vg_exiterr( const char *strErr );
15
16 #include "vg/vg_platform.h"
17 #include "vg/vg_io.h"
18 #include "vg/vg_audio.h"
19
20 #include "steam/steamworks_thin.h"
21
22 static inline float vg_get_axis( const char *axis ) __attribute__((unused));
23 static inline int vg_get_button( const char *button ) __attribute__((unused));
24 static inline int vg_get_button_down( const char *button ) __attribute__((unused));
25 static inline int vg_get_button_up( const char *button ) __attribute__((unused));
26
27 // Globals
28 GLFWwindow* vg_window;
29 int vg_window_x = 1280;
30 int vg_window_y = 720;
31
32 float vg_mouse_x;
33 float vg_mouse_y;
34
35 float vg_time;
36 float vg_time_last;
37 float vg_time_delta;
38
39 // Input
40 // ===========================================================================================================
41 GLFWgamepadstate vg_gamepad;
42 int vg_gamepad_ready = 0;
43 const char *vg_gamepad_name = NULL;
44 int vg_gamepad_id;
45
46 enum EInputMode
47 {
48 k_EInputMode_pc,
49 k_EInputMode_gamepad
50 }
51 vg_input_mode;
52
53 static struct axis_binding
54 {
55 const char *name;
56 union
57 {
58 int positive;
59 int bind;
60 };
61 int negative;
62
63 float value;
64 }
65 vg_axis_binds[];
66
67 static struct button_binding
68 {
69 const char *name;
70 int bind;
71
72 int value; int prev;
73 }
74 vg_button_binds[];
75
76 #include "vg/config.h"
77
78 #pragma GCC diagnostic push
79 #pragma GCC diagnostic ignored "-Wreturn-type"
80
81 static inline float vg_get_axis( const char *axis )
82 {
83 for( int i = 0; i < vg_list_size( vg_axis_binds ); i ++ )
84 {
85 if( !strcmp( axis, vg_axis_binds[i].name ) )
86 {
87 return vg_axis_binds[i].value;
88 }
89 }
90 }
91
92 static inline struct button_binding *vg_get_button_ptr( const char *button )
93 {
94 for( int i = 0; i < vg_list_size( vg_button_binds ); i ++ )
95 {
96 if( !strcmp( button, vg_button_binds[i].name ) )
97 {
98 return vg_button_binds + i;
99 }
100 }
101 }
102 #pragma GCC diagnostic pop
103
104 static inline int vg_get_button( const char *button )
105 {
106 return vg_get_button_ptr( button )->value;
107 }
108
109 static inline int vg_get_button_down( const char *button )
110 {
111 struct button_binding *bind = vg_get_button_ptr( button );
112 return bind->value & (bind->value ^ bind->prev);
113 }
114
115 static inline int vg_get_button_up( const char *button )
116 {
117 struct button_binding *bind = vg_get_button_ptr( button );
118 return bind->prev & (bind->value ^ bind->prev);
119 }
120
121 static inline int key_is_keyboard( int const id )
122 {
123 static_assert( GLFW_MOUSE_BUTTON_LAST < GLFW_KEY_SPACE, "GLFW: Mouse has too many buttons" );
124 return id > GLFW_MOUSE_BUTTON_LAST;
125 }
126
127 // Mouse AND Keyboard get button press
128 int get_button_cross_device( int const id )
129 {
130 if( key_is_keyboard( id ) )
131 {
132 return glfwGetKey( vg_window, id );
133 }
134 else
135 {
136 return glfwGetMouseButton( vg_window, id ) == GLFW_PRESS;
137 }
138 }
139
140 void vg_update_inputs(void)
141 {
142 // Update button inputs
143 for( int i = 0; i < vg_list_size( vg_button_binds ); i ++ )
144 {
145 struct button_binding *binding = vg_button_binds + i;
146 binding->prev = binding->value;
147
148 if( vg_input_mode == k_EInputMode_pc )
149 {
150 binding->value = get_button_cross_device( binding->bind );
151 }
152 else
153 {
154 binding->value = vg_gamepad.buttons[ binding->bind ];
155 }
156 }
157
158 // Update axis inputs
159 for( int i = 0; i < vg_list_size( vg_axis_binds ); i ++ )
160 {
161 struct axis_binding *binding = vg_axis_binds + i;
162
163 if( vg_input_mode == k_EInputMode_pc )
164 {
165 binding->value = get_button_cross_device( binding->positive );
166 binding->value -= get_button_cross_device( binding->negative );
167 }
168 else
169 {
170 binding->value = vg_gamepad.axes[ binding->bind ];
171 }
172 }
173 }
174
175 // Engine main
176 // ===========================================================================================================
177
178 #define VG_GAMELOOP __attribute__((weak))
179
180 void( *vg_on_exit[16] )(void);
181 u32 vg_exit_count = 0;
182
183 // Add a shutdown step
184 void vg_register_exit( void( *funcptr )(void), const char *name )
185 {
186 vg_info( "exit registered: (%u)'%s'\n", vg_exit_count, name );
187 vg_on_exit[ vg_exit_count ++ ] = funcptr;
188 }
189
190 void vg_exit(void)
191 {
192 for( int i = vg_exit_count-1; i >= 0; i -- )
193 {
194 vg_info( "engine_exit[%d]()\n", i );
195 vg_on_exit[i]();
196 }
197
198 vg_info( "done\n" );
199 }
200
201 // Forcefully exit program after error
202 void vg_exiterr( const char *strErr )
203 {
204 vg_error( "Engine Fatal: %s\n", strErr );
205 vg_exit();
206 exit(0);
207 }
208
209 // Callbacks
210 // ---------
211
212 void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos )
213 {
214 vg_mouse_x = xpos;
215 vg_mouse_y = ypos;
216 }
217
218 void vg_scroll_callback( GLFWwindow* ptrW, double xoffset, double yoffset )
219 {
220
221 }
222
223 void vg_framebuffer_resize_callback( GLFWwindow *ptrW, int w, int h )
224 {
225 vg_window_x = w;
226 vg_window_y = h;
227 }
228
229 void vg_start(void) VG_GAMELOOP;
230 void vg_update(void) VG_GAMELOOP;
231 void vg_render(void) VG_GAMELOOP;
232 void vg_ui(void) VG_GAMELOOP;
233 void vg_free(void) VG_GAMELOOP;
234
235 void vg_init( int argc, char *argv[], const char *window_name )
236 {
237 #ifdef VG_STEAM
238 // Initialize steamworks
239 if( !sw_init( 1218140U ) )
240 {
241 vg_exiterr( "Steamworks failed to initialize" );
242 }
243 else
244 {
245 vg_register_exit( &sw_SteamAPI_Shutdown, "SteamAPI" );
246 }
247 #endif
248
249 // Context creation
250 // ==========================================================================================================================
251 glfwInit();
252 glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 );
253 glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 );
254 glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE );
255 glfwWindowHint( GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE );
256
257 glfwWindowHint( GLFW_SAMPLES, 4 );
258
259 GLFWmonitor *monitor_primary = glfwGetPrimaryMonitor();
260
261 const GLFWvidmode *mode = glfwGetVideoMode( monitor_primary );
262 glfwWindowHint( GLFW_RED_BITS, mode->redBits );
263 glfwWindowHint( GLFW_GREEN_BITS, mode->greenBits );
264 glfwWindowHint( GLFW_BLUE_BITS, mode->blueBits );
265 glfwWindowHint( GLFW_REFRESH_RATE, mode->refreshRate );
266
267 if( !(vg_window = glfwCreateWindow( vg_window_x, vg_window_y, window_name, NULL, NULL)) )
268 {
269 vg_exiterr( "GLFW Failed to initialize" );
270 }
271 else
272 {
273 vg_register_exit( &glfwTerminate, "glfwTerminate" );
274 }
275
276 glfwMakeContextCurrent( vg_window );
277 glfwSwapInterval( 1 );
278
279 // Set callbacks
280 glfwSetFramebufferSizeCallback( vg_window, vg_framebuffer_resize_callback );
281
282 glfwSetCursorPosCallback( vg_window, vg_mouse_callback );
283 glfwSetScrollCallback( vg_window, vg_scroll_callback );
284
285 //glfwSetCharCallback( vg_window, console_proc_wchar );
286 //glfwSetKeyCallback( vg_window, console_proc_key );
287 //glfwSetInputMode(vg_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
288
289 if( !gladLoadGLLoader((GLADloadproc)glfwGetProcAddress) )
290 {
291 vg_exiterr( "Glad failed to initialize" );
292 }
293
294 const unsigned char* glver = glGetString( GL_VERSION );
295 vg_success( "Load setup complete, OpenGL version: %s\n", glver );
296
297 for( int id = 0; id <= GLFW_JOYSTICK_LAST; id ++ )
298 {
299 if( glfwJoystickIsGamepad( id ) )
300 {
301 vg_gamepad_name = glfwGetGamepadName( id );
302 vg_success( "Gamepad with mapping registered: %s\n", vg_gamepad_name );
303
304 vg_gamepad_ready = 1;
305 vg_gamepad_id = id;
306
307 return;
308 }
309 }
310
311 vg_audio_init();
312 vg_register_exit( &vg_audio_free, "vg_audio_free" );
313
314 if( vg_start ) vg_start();
315
316 // Main gameloop
317 while( !glfwWindowShouldClose( vg_window ) )
318 {
319 glfwPollEvents();
320
321 #ifdef VG_STEAM
322 sw_RunSteamEventLoop();
323 #endif
324
325 vg_time_last = vg_time;
326 vg_time = glfwGetTime();
327 vg_time_delta = vg_min( vg_time - vg_time_last, 0.1f );
328
329 vg_update_inputs();
330
331 if( vg_update ) vg_update();
332
333 // Update mashed projections etc
334
335 if( vg_render ) vg_render();
336 if( vg_ui ) vg_ui();
337
338 glfwSwapBuffers( vg_window );
339 }
340
341 if( vg_free ) vg_free();
342 vg_exit();
343 }
344
345 u32 NvOptimusEnablement = 0x00000001;
346 int AmdPowerXpressRequestHighPerformance = 1;