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