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