4 struct vg_engine vg
= {
6 .time_fixed_delta
= VG_TIMESTEP_FIXED
11 enum engine_status
_vg_engine_status(void)
13 SDL_AtomicLock( &vg
.sl_status
);
14 enum engine_status status
= vg
.engine_status
;
15 SDL_AtomicUnlock( &vg
.sl_status
);
20 enum vg_thread_purpose
vg_thread_purpose(void)
22 SDL_AtomicLock( &vg
.sl_status
);
24 if( vg
.thread_id_main
== SDL_GetThreadID(NULL
) ){
25 SDL_AtomicUnlock( &vg
.sl_status
);
26 return k_thread_purpose_main
;
29 SDL_AtomicUnlock( &vg
.sl_status
);
30 return k_thread_purpose_loader
;
34 static void vg_assert_thread( enum vg_thread_purpose required
)
36 enum vg_thread_purpose purpose
= vg_thread_purpose();
38 if( purpose
!= required
){
39 vg_fatal_error( "thread_purpose must be %u not %u\n", required
, purpose
);
43 static void _vg_opengl_sync_init(void)
45 vg
.sem_loader
= SDL_CreateSemaphore(1);
48 #include "vg_console.h"
49 #include "vg_profiler.h"
53 #include "vg_shader.h"
58 #include "vg_rigidbody_view.h"
59 #include "vg_loader.h"
63 static struct vg_profile vg_prof_update
= {.name
="update()"},
64 vg_prof_render
= {.name
="render()"},
65 vg_prof_swap
= {.name
="swap"};
67 void vg_checkgl( const char *src_info
)
72 while( (err
= glGetError()) != GL_NO_ERROR
){
73 vg_error( "(%s) OpenGL Error: #%d\n", src_info
, err
);
78 vg_fatal_error( "OpenGL Error" );
81 static void async_vg_bake_shaders( void *payload
, u32 size
)
86 void vg_bake_shaders(void)
88 vg_console_reg_cmd( "reload_shaders", vg_shaders_live_recompile
, NULL
);
89 vg_async_call( async_vg_bake_shaders
, NULL
, 0 );
92 void async_internal_complete( void *payload
, u32 size
)
94 vg_success( "Internal async setup complete\n" );
95 SDL_AtomicLock( &vg
.sl_status
);
97 if( vg
.engine_status
== k_engine_status_crashed
){
98 SDL_AtomicUnlock( &vg
.sl_status
);
102 vg
.engine_status
= k_engine_status_running
;
105 SDL_AtomicUnlock( &vg
.sl_status
);
108 #ifdef VG_CUSTOM_SHADERS
109 void vg_auto_shader_register(void); /* created from codegen */
112 static void _vg_load_full( void *data
)
115 vg_tex2d_replace_with_error_async( &vg
.tex_missing
);
117 vg_ui
.tex_bg
= vg
.tex_missing
;
120 vg_loader_step( vg_input_init
, vg_input_free
);
121 vg_loader_step( vg_lines_init
, NULL
);
122 vg_loader_step( vg_rb_view_init
, NULL
);
124 vg_loader_step( vg_audio_init
, vg_audio_free
);
126 vg_loader_step( vg_profiler_init
, NULL
);
129 #ifdef VG_CUSTOM_SHADERS
130 vg_auto_shader_register();
134 vg_async_call( async_internal_complete
, NULL
, 0 );
136 vg_success( "Client loaded in %fs\n", vg
.time_real
);
139 static void _vg_process_events(void)
141 v2_zero( vg
.mouse_wheel
);
142 v2_zero( vg
.mouse_delta
);
149 while( SDL_PollEvent( &event
) ){
150 if( event
.type
== SDL_KEYDOWN
){
151 if( vg_console
.enabled
&&
152 (vg_ui
.focused_control_type
!= k_ui_control_modal
) ){
153 if( event
.key
.keysym
.sym
== SDLK_ESCAPE
||
154 event
.key
.keysym
.scancode
== SDL_SCANCODE_GRAVE
){
155 vg_console
.enabled
= 0;
158 else if( (event
.key
.keysym
.mod
& KMOD_CTRL
) &&
159 event
.key
.keysym
.sym
== SDLK_n
){
160 console_suggest_next();
162 else if( (event
.key
.keysym
.mod
& KMOD_CTRL
) &&
163 event
.key
.keysym
.sym
== SDLK_p
){
164 console_suggest_prev();
167 ui_proc_key( event
.key
.keysym
);
171 if( event
.key
.keysym
.scancode
== SDL_SCANCODE_GRAVE
){
172 vg_console
.enabled
= 1;
175 ui_proc_key( event
.key
.keysym
);
179 else if( event
.type
== SDL_MOUSEWHEEL
){
180 vg
.mouse_wheel
[0] += event
.wheel
.preciseX
;
181 vg
.mouse_wheel
[1] += event
.wheel
.preciseY
;
183 else if( event
.type
== SDL_CONTROLLERDEVICEADDED
||
184 event
.type
== SDL_CONTROLLERDEVICEREMOVED
)
186 vg_input_device_event( &event
);
188 else if( event
.type
== SDL_CONTROLLERAXISMOTION
||
189 event
.type
== SDL_CONTROLLERBUTTONDOWN
||
190 event
.type
== SDL_CONTROLLERBUTTONUP
)
192 vg_input_controller_event( &event
);
194 else if( event
.type
== SDL_MOUSEMOTION
){
195 vg
.mouse_delta
[0] += event
.motion
.xrel
;
196 vg
.mouse_delta
[1] += event
.motion
.yrel
;
198 else if( event
.type
== SDL_WINDOWEVENT
){
199 if( event
.window
.event
== SDL_WINDOWEVENT_SIZE_CHANGED
){
201 SDL_GL_GetDrawableSize( vg
.window
, &w
, &h
);
204 vg_warn( "Got a invalid framebuffer size: "
205 "%dx%d... ignoring\n", w
, h
);
211 vg_framebuffer_resize(w
,h
);
214 else if( event
.window
.event
== SDL_WINDOWEVENT_CLOSE
){
215 vg
.window_should_close
= 1;
218 else if( event
.type
== SDL_TEXTINPUT
){
219 ui_proc_utf8( event
.text
.text
);
223 SDL_GetMouseState( &vg
.mouse_pos
[0], &vg
.mouse_pos
[1] );
226 static void _vg_gameloop_update(void)
228 vg_profile_begin( &vg_prof_update
);
230 vg
.engine_stage
= k_engine_stage_update
;
233 /* Fixed update loop */
234 vg
.engine_stage
= k_engine_stage_update_fixed
;
236 vg
.fixed_iterations
= 0;
237 vg_lines
.enabled
= vg_lines
.render
;
238 vg
.time_fixed_accumulator
+= vg
.time_delta
;
240 while( vg
.time_fixed_accumulator
>= vg
.time_fixed_delta
){
242 vg_lines
.enabled
= 0;
243 vg
.time_fixed_accumulator
-= vg
.time_fixed_delta
;
245 vg
.fixed_iterations
++;
246 if( vg
.fixed_iterations
== 8 ){
250 vg_lines
.enabled
= vg_lines
.render
;
251 vg
.time_fixed_extrapolate
= vg
.time_fixed_accumulator
/ vg
.time_fixed_delta
;
253 vg
.engine_stage
= k_engine_stage_update
;
255 vg_profile_end( &vg_prof_update
);
258 static void vg_settings_gui(void);
259 static void _vg_gameloop_render(void)
261 vg_profile_begin( &vg_prof_render
);
264 vg
.engine_stage
= k_engine_stage_rendering
;
267 vg_profile_end( &vg_prof_render
);
270 vg
.engine_stage
= k_engine_stage_ui
;
273 if( vg_console
.enabled
){
274 vg_ui
.ignore_input_frames
= 10;
276 vg_ui
.ignore_input_frames
= 0;
277 vg_ui
.wants_mouse
= 1;
282 if( vg
.settings_open
)
287 audio_debug_ui( vg
.pv
);
292 int frame_target
= vg
.display_refresh_rate
;
293 if( vg
.fps_limit
> 0 ) frame_target
= vg
.fps_limit
;
295 (struct vg_profile
*[]){
296 &vg_prof_update
,&vg_prof_render
,&vg_prof_swap
}, 3,
297 (1.0f
/(float)frame_target
)*1000.0f
,
298 (ui_rect
){ 4, 4, 250, 0 }, 0, 0
304 "refresh: %d (%.1fms)\n"
306 "iterations: %d (acc: %.3fms%%)\n"
307 "time: real(%.2f) delta(%.2f) rate(%.2f)\n"
308 " extrap(%.2f) frame(%.2f) spin( "PRINTF_U64
" )\n",
309 vg
.window_x
, vg
.window_y
,
310 frame_target
, (1.0f
/(float)frame_target
)*1000.0f
,
313 (vg
.time_fixed_accumulator
/VG_TIMESTEP_FIXED
)*100.0f
,
314 vg
.time_real
, vg
.time_delta
, vg
.time_rate
,
315 vg
.time_fixed_extrapolate
, vg
.time_frame_delta
,
318 ui_text( (ui_rect
){258,4,900,900},perf
,1,0,k_ui_align_left
);
324 static void vg_changevsync(void){
325 if( vg
.vsync
&& (vg
.vsync_feature
!= k_vsync_feature_error
) ){
326 /* turn on vsync if not enabled */
328 enum vsync_feature requested
= k_vsync_feature_enabled
;
329 if( vg
.vsync
< 0 ) requested
= k_vsync_feature_enabled_adaptive
;
331 if( vg
.vsync_feature
!= requested
){
332 vg_info( "Setting swap interval\n" );
334 int swap_interval
= 1;
335 if( requested
== k_vsync_feature_enabled_adaptive
)
338 if( SDL_GL_SetSwapInterval( swap_interval
) == -1 ){
339 if( requested
== k_vsync_feature_enabled
){
340 vg_error( "Vsync is not supported by your system\n" );
341 vg_warn( "You may be overriding it in your"
342 " graphics control panel.\n" );
345 vg_error( "Adaptive Vsync is not supported by your system\n" );
348 vg
.vsync_feature
= k_vsync_feature_error
;
350 /* TODO: Make popup to notify user that this happened */
353 vg_success( "Vsync enabled (%d)\n", requested
);
354 vg
.vsync_feature
= requested
;
359 if( vg
.vsync_feature
!= k_vsync_feature_disabled
){
360 SDL_GL_SetSwapInterval( 0 );
361 vg
.vsync_feature
= k_vsync_feature_disabled
;
366 static int vg_framefilter( double dt
){
367 if( vg
.fps_limit
< 24 ) vg
.fps_limit
= 24;
368 if( vg
.fps_limit
> 300 ) vg
.fps_limit
= 300;
370 double min_frametime
= 1.0/(double)vg
.fps_limit
;
371 if( vg
.time_frame_delta
< min_frametime
){
372 /* TODO: we can use high res nanosleep on Linux here */
373 double sleep_ms
= (min_frametime
-vg
.time_frame_delta
) * 1000.0;
374 u32 ms
= (u32
)floor( sleep_ms
);
377 if( !vg_loader_availible() )
392 static int _vg_crashscreen(void)
395 if( vg_getkey( SDLK_ESCAPE
) )
399 glBindFramebuffer( GL_FRAMEBUFFER
, 0 );
401 glDisable(GL_DEPTH_TEST
);
402 glBlendFunc(GL_ONE_MINUS_DST_ALPHA
, GL_DST_ALPHA
);
403 glBlendEquation(GL_FUNC_ADD
);
405 glClearColor( 0.15f
+ sinf(vg
.time_real
)*0.1f
, 0.0f
, 0.0f
,1.0f
);
406 glClear( GL_COLOR_BUFFER_BIT
);
407 glViewport( 0,0, vg
.window_x
, vg
.window_y
);
416 static void _vg_gameloop(void){
417 //vg.time_fixed_accumulator = 0.75f * (1.0f/60.0f);
419 vg
.time_hp
= SDL_GetPerformanceCounter();
420 vg
.time_hp_last
= vg
.time_hp
;
424 vg
.time_hp
= SDL_GetPerformanceCounter();
425 u64 udt
= vg
.time_hp
- vg
.time_hp_last
;
426 vg
.time_hp_last
= vg
.time_hp
;
428 double dt
= (double)udt
/ (double)SDL_GetPerformanceFrequency();
430 vg
.time_frame_delta
+= dt
;
431 vg_run_async_checked();
433 if( vg_framefilter( dt
) )
438 enum engine_status status
= _vg_engine_status();
439 if( status
== k_engine_status_running
)
440 vg_profile_begin( &vg_prof_swap
);
442 SDL_GL_SwapWindow( vg
.window
);
444 if( status
== k_engine_status_running
)
445 vg_profile_end( &vg_prof_swap
);
447 vg
.time_real
+= vg
.time_frame_delta
;
448 vg
.time_delta
= vg
.time_frame_delta
* vg
.time_rate
;
449 vg
.time
+= vg
.time_delta
;
451 _vg_process_events();
453 if( vg
.window_should_close
)
456 if( status
== k_engine_status_crashed
){
457 if( _vg_crashscreen() )
461 if( status
== k_engine_status_running
){
462 _vg_gameloop_update();
463 _vg_gameloop_render();
470 if( vg
.loader_ring
> 0.01f
){
471 vg_loader_render_ring( vg
.loader_ring
);
472 vg
.loader_ring
-= vg
.time_frame_delta
* 0.5f
;
475 vg
.time_frame_delta
= 0.0;
476 vg
.time_spinning
= 0;
480 static void _vg_process_launch_opts_internal( int argc
, char *argv
[] )
483 while( vg_argp( argc
, argv
) ){
484 if( (arg
= vg_opt_arg( 'w' )) ){
485 vg
.window_x
= atoi( arg
);
488 if( (arg
= vg_opt_arg( 'h' )) ){
489 vg
.window_y
= atoi( arg
);
492 if( (arg
= vg_long_opt_arg( "samples" )) ){
493 vg
.samples
= VG_MAX( 0, VG_MIN( 8, atoi( arg
) ) );
496 if( vg_long_opt( "use-libc-malloc" ) ){
497 vg_mem
.use_libc_malloc
= 1;
500 if( vg_long_opt( "high-performance" ) ){
501 vg
.quality_profile
= k_quality_profile_low
;
508 static void _vg_init_window( const char *window_name
)
510 vg_info( "SDL_INIT\n" );
512 if( SDL_Init( SDL_INIT_VIDEO
) != 0 ){
513 vg_error( "SDL_Init failed: %s\n", SDL_GetError() );
518 SDL_InitSubSystem( SDL_INIT_AUDIO
);
520 SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER
);
522 char *exe_basepath
= SDL_GetBasePath();
523 u32 len
= vg_align8( strlen(exe_basepath
)+1 );
524 char *dest
= vg_linear_alloc( vg_mem
.rtmemory
, len
);
525 strcpy( dest
, exe_basepath
);
526 SDL_free( exe_basepath
);
529 vg_info( "Basepath: %s\n", vg
.base_path
);
531 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER
, 1 );
532 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION
, 3 );
533 SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION
, 3 );
534 SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK
,
535 SDL_GL_CONTEXT_PROFILE_CORE
);
537 SDL_GL_SetAttribute( SDL_GL_CONTEXT_RELEASE_BEHAVIOR
,
538 SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH
);
540 SDL_GL_SetAttribute( SDL_GL_RED_SIZE
, 8 );
541 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE
, 8 );
542 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE
, 8 );
543 SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE
, 8 );
544 SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE
, 0 );
547 * Get monitor information
549 vg_info( "Getting display count\n" );
550 int display_count
= 0,
554 SDL_DisplayMode video_mode
;
555 if( SDL_GetDesktopDisplayMode( display_index
, &video_mode
) ){
556 vg_error( "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError() );
561 vg
.display_refresh_rate
= video_mode
.refresh_rate
;
562 vg
.window_x
= video_mode
.w
;
563 vg
.window_y
= video_mode
.h
;
565 if( vg
.screen_mode
== 2 ){
571 SDL_SetHint( "SDL_VIDEO_X11_XINERAMA", "1" );
572 SDL_SetHint( "SDL_VIDEO_X11_XRANDR", "0" );
573 SDL_SetHint( "SDL_VIDEO_X11_XVIDMODE", "0" );
576 u32 flags
= SDL_WINDOW_OPENGL
| SDL_WINDOW_INPUT_GRABBED
|
577 SDL_WINDOW_RESIZABLE
;
579 if( vg
.screen_mode
== 1 )
580 flags
|= SDL_WINDOW_FULLSCREEN
;
581 else if( vg
.screen_mode
== 0 )
582 flags
|= SDL_WINDOW_FULLSCREEN_DESKTOP
;
584 vg_info( "CreateWindow( %d %d %u )\n", vg
.window_x
, vg
.window_y
, flags
);
586 if((vg
.window
= SDL_CreateWindow( window_name
, 0, 0,
587 vg
.window_x
, vg
.window_y
, flags
))){
588 if( vg
.screen_mode
== 2 )
589 SDL_SetWindowPosition( vg
.window
, video_mode
.w
-vg
.window_x
, 0 );
592 vg_error( "SDL_CreateWindow failed: %s", SDL_GetError() );
596 SDL_RaiseWindow( vg
.window
);
597 SDL_SetWindowMinimumSize( vg
.window
, 1280, 720 );
598 SDL_SetWindowMaximumSize( vg
.window
, 4096, 4096 );
600 vg_info( "CreateContext\n" );
603 if( SDL_IsTextInputActive() ) SDL_StopTextInput();
608 if( (vg
.gl_context
= SDL_GL_CreateContext(vg
.window
) )){
609 SDL_GL_GetDrawableSize( vg
.window
, &vg
.window_x
, &vg
.window_y
);
610 vg_success( "Window created (%dx%d)\n", vg
.window_x
, vg
.window_y
);
613 vg_error( "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
618 if( !gladLoadGLLoader((GLADloadproc
)SDL_GL_GetProcAddress
) ) {
619 vg_error( "Glad Failed to initialize\n" );
620 SDL_GL_DeleteContext( vg
.gl_context
);
625 const unsigned char* glver
= glGetString( GL_VERSION
);
626 vg_success( "Load setup complete, OpenGL version: %s\n", glver
);
628 SDL_GL_SetSwapInterval(0); /* disable vsync while loading */
630 SDL_DisplayMode dispmode
;
631 if( !SDL_GetWindowDisplayMode( vg
.window
, &dispmode
) ){
632 if( dispmode
.refresh_rate
){
633 vg
.display_refresh_rate
= dispmode
.refresh_rate
;
637 if( vg
.display_refresh_rate
< 25 || vg
.display_refresh_rate
> 300 ){
638 vg
.display_refresh_rate
= 60;
641 vg_info( "Display refresh rate: %d\n", dispmode
.refresh_rate
);
642 if( !vg
.fps_limit
) vg
.fps_limit
= vg
.display_refresh_rate
;
645 static void _vg_terminate(void)
648 vg_console_write_persistent();
650 SDL_AtomicLock( &vg
.sl_status
);
651 vg
.engine_status
= k_engine_status_none
;
652 SDL_AtomicUnlock( &vg
.sl_status
);
656 vg_success( "If you see this it means everything went.. \"well\".....\n" );
658 SDL_GL_DeleteContext( vg
.gl_context
);
663 static int cmd_vg_settings_toggle( int argc
, const char *argv
[] );
664 void vg_enter( int argc
, char *argv
[], const char *window_name
)
666 vg_rand_seed( &vg
.rand
, 461 );
667 _vg_process_launch_opts_internal( argc
, argv
);
673 vg_console_reg_var( "vg_fps_limit", &vg
.fps_limit
,
674 k_var_dtype_i32
, VG_VAR_PERSISTENT
);
675 vg_console_reg_var( "vg_vsync", &vg
.vsync
,
676 k_var_dtype_i32
, VG_VAR_PERSISTENT
);
677 vg_console_reg_var( "vg_quality", &vg
.quality_profile
,
678 k_var_dtype_i32
, VG_VAR_PERSISTENT
);
679 vg_console_reg_var( "vg_screen_mode", &vg
.screen_mode
,
680 k_var_dtype_i32
, VG_VAR_PERSISTENT
);
682 vg_console_load_autos();
684 vg_console_reg_cmd( "vg_settings", cmd_vg_settings_toggle
, NULL
);
685 _vg_init_window( window_name
);
688 SDL_SetRelativeMouseMode(1);
690 vg
.thread_id_main
= SDL_GetThreadID(NULL
);
692 /* Opengl-required systems */
696 vg
.engine_status
= k_engine_status_load_internal
;
698 _vg_opengl_sync_init();
699 vg_loader_start( _vg_load_full
, NULL
);
704 void vg_fatal_error( const char *fmt
, ... )
707 va_start( args
, fmt
);
708 _vg_logx_va( stderr
, NULL
, "fatal", KRED
, fmt
, args
);
711 vg_print_backtrace();
713 SDL_AtomicLock( &vg
.sl_status
);
714 vg
.engine_status
= k_engine_status_crashed
;
715 SDL_AtomicUnlock( &vg
.sl_status
);
717 if( vg_thread_purpose() == k_thread_purpose_loader
)
719 longjmp( vg
.env_loader_exit
, 1 );
723 vg_error( "There is no jump to the error runner thing yet! bai bai\n" );
730 * ---------------------------------------------------------------------------
733 #ifdef VG_GAME_SETTINGS
734 extern void vg_game_settings_gui( ui_rect panel
) ;
735 extern void vg_game_settings_init(void);
738 struct ui_enum_opt vg_settings_vsync_enum
[] = {
744 struct ui_enum_opt vg_settings_quality_enum
[] = {
745 { 0, "High Quality" },
747 { 2, "Absolute Minimum" },
750 struct ui_enum_opt vg_settings_screen_mode_enum
[] = {
751 { 0, "Fullscreen (desktop)" },
752 { 1, "Fullscreen (native)" },
753 { 2, "Floating Window" }
756 struct ui_enum_opt vg_settings_dsp_enum
[] = {
762 struct vg_setting_ranged_i32 fps_limit
;
763 struct vg_setting_enum vsync
, quality
, screenmode
, audio_devices
, dsp
;
764 i32 temp_audio_choice
;
766 static vg_settings
= {
767 .fps_limit
= { .label
= "Fps Limit",
768 .min
=24, .max
=300, .actual_value
= &vg
.fps_limit
},
769 .vsync
= { .label
= "Vsync",
770 .actual_value
= &vg
.vsync
,
771 .options
= vg_settings_vsync_enum
, .option_count
= 3 },
772 .quality
= { .label
= "Graphic Quality",
773 .actual_value
= &vg
.quality_profile
,
774 .options
= vg_settings_quality_enum
, .option_count
= 3 },
775 .screenmode
= { .label
= "Type",
776 .actual_value
= &vg
.screen_mode
,
777 .options
= vg_settings_screen_mode_enum
, .option_count
=3 },
778 .audio_devices
= { .label
= "Audio Device",
779 .actual_value
= &vg_settings
.temp_audio_choice
,
780 .options
= NULL
, .option_count
= 0 },
781 .dsp
= { .label
= "Audio effects (reverb etc.)",
782 .actual_value
= &vg_audio
.dsp_enabled
,
783 .options
= vg_settings_dsp_enum
, .option_count
=2 },
786 static void vg_settings_ui_draw_diff( ui_rect orig
){
788 ui_split( orig
, k_ui_axis_v
, -32, 0, l
, r
);
789 ui_text( r
, "*", 1, k_ui_align_middle_center
, ui_colour(k_ui_blue
) );
793 * ------------------------------------------------------------------------- */
795 static void vg_settings_ui_int( char *buf
, u32 len
){
796 for( u32 i
=0, j
=0; i
<len
; i
++ ){
797 if( ((buf
[i
] >= '0') && (buf
[i
] <= '9')) || (buf
[i
] == '\0') )
802 struct ui_textbox_callbacks
static vg_settings_ui_int_callbacks
= {
803 .change
= vg_settings_ui_int
806 static bool vg_settings_ranged_i32_valid( struct vg_setting_ranged_i32
*prop
){
807 if( prop
->new_value
< prop
->min
) return 0;
808 if( prop
->new_value
> prop
->max
) return 0;
812 static bool vg_settings_ranged_i32_diff( struct vg_setting_ranged_i32
*prop
){
813 if( prop
->new_value
!= *prop
->actual_value
) return 1;
817 static bool vg_settings_ui_ranged_i32( struct vg_setting_ranged_i32
*prop
,
820 rect_copy( rect
, orig
);
822 ui_textbox( rect
, prop
->label
, prop
->buf
, sizeof(prop
->buf
),
823 1, 0, &vg_settings_ui_int_callbacks
);
824 prop
->new_value
= atoi( prop
->buf
);
826 if( vg_settings_ranged_i32_diff( prop
) )
827 vg_settings_ui_draw_diff( orig
);
829 bool valid
= vg_settings_ranged_i32_valid( prop
);
832 ui_split( orig
, k_ui_axis_h
, -1, 0, _null
, line
);
835 ui_fill( line
, ui_colour( k_ui_red
) );
841 void ui_settings_ranged_i32_init( struct vg_setting_ranged_i32
*prop
)
844 vg_strnull( &tmp
, prop
->buf
, sizeof(prop
->buf
) );
845 vg_strcati32( &tmp
, *prop
->actual_value
);
846 prop
->new_value
= *prop
->actual_value
;
850 * ------------------------------------------------------------------------- */
852 bool vg_settings_enum_diff( struct vg_setting_enum
*prop
)
854 if( prop
->new_value
!= *prop
->actual_value
) return 1;
858 bool vg_settings_enum( struct vg_setting_enum
*prop
, ui_rect rect
)
861 rect_copy( rect
, orig
);
863 ui_enum( rect
, prop
->label
,
864 prop
->options
, prop
->option_count
, &prop
->new_value
);
866 if( vg_settings_enum_diff( prop
) )
867 vg_settings_ui_draw_diff( orig
);
872 void ui_settings_enum_init( struct vg_setting_enum
*prop
)
874 prop
->new_value
= *prop
->actual_value
;
879 void vg_settings_ui_header( ui_rect inout_panel
, const char *name
)
882 ui_standard_widget( inout_panel
, rect
, 2 );
883 ui_text( rect
, name
, 1, k_ui_align_middle_center
, ui_colour(k_ui_fg
+3) );
887 bool vg_settings_apply_button( ui_rect inout_panel
, bool validated
)
890 ui_px height
= (vg_ui
.font
->glyph_height
+ 18) * k_ui_scale
;
891 ui_split( inout_panel
, k_ui_axis_h
, -height
, k_ui_padding
,
892 inout_panel
, last_row
);
894 const char *string
= "Apply";
896 if( ui_button( last_row
, string
) == 1 )
901 ui_standard_widget( last_row
, rect
, 1 );
902 ui_fill( rect
, ui_colour( k_ui_bg
+1 ) );
903 ui_outline( rect
, -1, ui_colour( k_ui_red
), 0 );
905 ui_rect t
= { 0,0, ui_text_line_width( string
), 14 };
906 ui_rect_center( rect
, t
);
907 ui_text( t
, string
, 1, k_ui_align_left
, ui_colour(k_ui_fg
+3) );
913 static void vg_settings_video_apply(void){
914 if( vg_settings_enum_diff( &vg_settings
.screenmode
) ){
915 vg
.screen_mode
= vg_settings
.screenmode
.new_value
;
917 if( (vg
.screen_mode
== 0) || (vg
.screen_mode
== 1) ){
918 SDL_DisplayMode video_mode
;
919 if( SDL_GetDesktopDisplayMode( 0, &video_mode
) ){
920 vg_error("SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError());
923 //vg.display_refresh_rate = video_mode.refresh_rate;
924 vg
.window_x
= video_mode
.w
;
925 vg
.window_y
= video_mode
.h
;
927 SDL_SetWindowSize( vg
.window
, vg
.window_x
, vg
.window_y
);
930 if( vg
.screen_mode
== 0 )
931 SDL_SetWindowFullscreen( vg
.window
, SDL_WINDOW_FULLSCREEN_DESKTOP
);
932 if( vg
.screen_mode
== 1 )
933 SDL_SetWindowFullscreen( vg
.window
, SDL_WINDOW_FULLSCREEN
);
934 if( vg
.screen_mode
== 2 ){
935 SDL_SetWindowFullscreen( vg
.window
, 0 );
936 SDL_SetWindowSize( vg
.window
, 1280, 720 );
937 SDL_SetWindowPosition( vg
.window
, 16, 16 );
938 SDL_SetWindowMinimumSize( vg
.window
, 1280, 720 );
939 SDL_SetWindowMaximumSize( vg
.window
, 4096, 4096 );
943 vg
.fps_limit
= vg_settings
.fps_limit
.new_value
;
944 vg
.quality_profile
= vg_settings
.quality
.new_value
;
945 vg
.vsync
= vg_settings
.vsync
.new_value
;
948 static void vg_settings_video_gui( ui_rect panel
){
951 ui_standard_widget( panel
, rq
, 1 );
952 vg_settings_enum( &vg_settings
.quality
, rq
);
956 if( vg
.vsync_feature
== k_vsync_feature_error
){
957 ui_info( panel
, "There was an error activating vsync feature." );
962 vg_settings_ui_header( panel
, "Frame Timing" );
964 ui_standard_widget( panel
, duo
, 1 );
965 ui_split_ratio( duo
, k_ui_axis_v
, 0.5f
, 16, d0
, d1
);
967 vg_settings_enum( &vg_settings
.vsync
, d0
);
968 validated
&= vg_settings_ui_ranged_i32( &vg_settings
.fps_limit
, d1
);
971 ui_standard_widget( panel
, duo
, 10 );
972 int frame_target
= vg
.display_refresh_rate
;
973 if( !vg
.vsync
) frame_target
= vg
.fps_limit
;
975 (struct vg_profile
*[]){
976 &vg_prof_update
,&vg_prof_render
,&vg_prof_swap
}, 3,
977 (1.0f
/(f32
)frame_target
)*1500.0f
,
981 ui_fill( (ui_rect
){ duo
[0], duo
[1]+(duo
[3]*2)/3, duo
[2], 1 },
982 ui_colour(k_ui_fg
) );
985 vg_settings_ui_header( panel
, "Window Specification" );
987 ui_standard_widget( panel
, duo
, 1 );
988 vg_settings_enum( &vg_settings
.screenmode
, duo
);
990 if( vg_settings_apply_button( panel
, validated
) )
991 vg_settings_video_apply();
994 static void vg_settings_audio_apply(void){
995 if( vg_settings_enum_diff( &vg_settings
.audio_devices
) ){
996 if( vg_audio
.sdl_output_device
){
997 vg_info( "Closing audio device %d\n", vg_audio
.sdl_output_device
);
998 SDL_CloseAudioDevice( vg_audio
.sdl_output_device
);
1001 vg_strfree( &vg_audio
.device_choice
);
1003 if( vg_settings
.audio_devices
.new_value
== -1 ){ }
1004 else if( vg_settings
.audio_devices
.new_value
== -2 ){
1005 vg_fatal_error( "Programming error\n" );
1008 struct ui_enum_opt
*selected
= NULL
, *oi
;
1010 for( int i
=0; i
<vg_settings
.audio_devices
.option_count
; i
++ ){
1011 oi
= &vg_settings
.audio_devices
.options
[i
];
1013 if( oi
->value
== vg_settings
.audio_devices
.new_value
){
1019 vg_strnull( &vg_audio
.device_choice
, NULL
, -1 );
1020 vg_strcat( &vg_audio
.device_choice
, oi
->alias
);
1023 vg_audio_device_init();
1024 *vg_settings
.audio_devices
.actual_value
=
1025 vg_settings
.audio_devices
.new_value
;
1029 if( vg_settings_enum_diff( &vg_settings
.dsp
) ){
1030 *vg_settings
.dsp
.actual_value
=
1031 vg_settings
.dsp
.new_value
;
1037 static void vg_settings_audio_gui( ui_rect panel
){
1039 ui_standard_widget( panel
, rq
, 1 );
1040 vg_settings_enum( &vg_settings
.audio_devices
, rq
);
1042 ui_standard_widget( panel
, rq
, 1 );
1043 vg_settings_enum( &vg_settings
.dsp
, rq
);
1045 if( vg_settings_apply_button( panel
, 1 ) )
1046 vg_settings_audio_apply();
1049 void vg_settings_open(void)
1051 vg
.settings_open
= 1;
1053 ui_settings_ranged_i32_init( &vg_settings
.fps_limit
);
1054 ui_settings_enum_init( &vg_settings
.vsync
);
1055 ui_settings_enum_init( &vg_settings
.quality
);
1056 ui_settings_enum_init( &vg_settings
.screenmode
);
1058 /* Create audio options */
1059 int count
= SDL_GetNumAudioDevices( 0 );
1061 struct ui_enum_opt
*options
= malloc( sizeof(struct ui_enum_opt
)*(count
+1) );
1062 vg_settings
.audio_devices
.options
= options
;
1063 vg_settings
.audio_devices
.option_count
= count
+1;
1065 struct ui_enum_opt
*o0
= &options
[0];
1066 o0
->alias
= "OS Default";
1069 for( int i
=0; i
<count
; i
++ ){
1070 struct ui_enum_opt
*oi
= &options
[i
+1];
1072 const char *device_name
= SDL_GetAudioDeviceName( i
, 0 );
1073 int len
= strlen(device_name
);
1075 oi
->alias
= malloc( len
+1 );
1076 memcpy( (void *)oi
->alias
, device_name
, len
+1 );
1080 if( vg_audio
.device_choice
.buffer
){
1081 vg_settings
.temp_audio_choice
= -2;
1083 for( int i
=0; i
<count
; i
++ ){
1084 struct ui_enum_opt
*oi
= &options
[i
+1];
1085 if( !strcmp( oi
->alias
, vg_audio
.device_choice
.buffer
) ){
1086 vg_settings
.temp_audio_choice
= oi
->value
;
1092 vg_settings
.temp_audio_choice
= -1;
1095 ui_settings_enum_init( &vg_settings
.audio_devices
);
1096 ui_settings_enum_init( &vg_settings
.dsp
);
1098 #ifdef VG_GAME_SETTINGS
1099 vg_game_settings_init();
1103 void vg_settings_close(void)
1105 vg
.settings_open
= 0;
1107 struct ui_enum_opt
*options
= vg_settings
.audio_devices
.options
;
1108 for( int i
=1; i
< vg_settings
.audio_devices
.option_count
; i
++ )
1109 free( (void *)options
[i
].alias
);
1110 free( vg_settings
.audio_devices
.options
);
1113 static void vg_settings_gui(void)
1116 ui_rect screen
= { 0, 0, vg
.window_x
, vg
.window_y
};
1117 ui_rect window
= { 0, 0, 1000, 700 };
1118 ui_rect_center( screen
, window
);
1119 vg_ui
.wants_mouse
= 1;
1121 ui_fill( window
, ui_colour( k_ui_bg
+1 ) );
1122 ui_outline( window
, 1, ui_colour( k_ui_bg
+7 ), 0 );
1124 ui_rect title
, panel
;
1125 ui_split( window
, k_ui_axis_h
, 28, 0, title
, panel
);
1126 ui_fill( title
, ui_colour( k_ui_bg
+7 ) );
1127 ui_text( title
, "Settings", 1, k_ui_align_middle_center
,
1128 ui_colourcont(k_ui_bg
+7) );
1130 ui_rect quit_button
;
1131 ui_split( title
, k_ui_axis_v
, title
[2]-title
[3], 2, title
, quit_button
);
1133 if( ui_button_text( quit_button
, "X", 1 ) == 1 ){
1134 vg_settings_close();
1138 ui_rect_pad( panel
, (ui_px
[2]){ 8, 8 } );
1140 const char *opts
[] = { "video", "audio",
1141 #ifdef VG_GAME_SETTINGS
1146 static i32 page
= 0;
1147 ui_tabs( panel
, panel
, opts
, vg_list_size(opts
), &page
);
1150 vg_settings_video_gui( panel
);
1152 else if( page
== 1 )
1153 vg_settings_audio_gui( panel
);
1155 #ifdef VG_GAME_SETTINGS
1156 else if( page
== 2 )
1157 vg_game_settings_gui( panel
);
1161 static int cmd_vg_settings_toggle( int argc
, const char *argv
[] ){
1167 * Graphic cards will check these to force it to use the GPU.
1168 * TODO: explicit declexport. since -flto strips these symbols in release.
1170 u32 NvOptimusEnablement
= 0x00000001;
1171 int AmdPowerXpressRequestHighPerformance
= 1;
1173 #include "vg_async.c"
1174 #include "vg_audio.c"
1175 #include "vg_audio_dsp.c"
1176 #include "vg_audio_synth_bird.c"
1177 #include "vg_binstr.c"
1179 #include "vg_camera.c"
1180 #include "vg_lines.c"
1181 #include "vg_console.c"
1182 #include "vg_imgui.c"
1183 #include "vg_input.c"
1185 #include "vg_loader.c"
1189 #include "vg_mem_pool.c"
1190 #include "vg_mem_queue.c"
1193 #include "vg_perlin.c"
1194 #include "vg_string.c"
1195 #include "vg_profiler.c"
1196 #include "vg_rigidbody_collision.c"
1197 #include "vg_rigidbody_constraints.c"
1198 #include "vg_rigidbody.c"
1199 #include "vg_rigidbody_view.c"
1200 #include "vg_shader.c"
1202 #ifdef VG_CUSTOM_SHADERS
1203 #include "shaders/impl.c"