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