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