add motion vectors to all shaders
[carveJwlIkooP6JGAAIwe30JlM.git] / menu.h
1 #ifndef MENU_H
2 #define MENU_H
3
4 #include "common.h"
5 #include "model.h"
6 #include "world_render.h"
7 #include "player.h"
8
9 #include "shaders/menu.h"
10 #include "vg_steam_friends.h"
11 #include "submodules/tinydir/tinydir.h"
12
13 VG_STATIC mdl_context menu_model;
14 VG_STATIC glmesh menu_glmesh;
15 VG_STATIC m4x3f menu_mdl_mtx;
16 VG_STATIC float menu_opacity = 0.0f;
17 VG_STATIC float menu_input_cooldown = 0.0f;
18 VG_STATIC float menu_fov_target = 97.0f;
19 VG_STATIC v2f menu_extra_angles;
20
21 VG_STATIC int cl_menu = 0,
22 cl_menu_go_away = 0;
23
24 VG_STATIC int menu_enabled(void){ return cl_menu; }
25
26 VG_STATIC const char *playermodels[] = { "ch_new", "ch_jordan", "ch_outlaw" };
27
28 vg_tex2d tex_menu = { .path = "textures/menu.qoi",.flags = VG_TEXTURE_NEAREST };
29
30 VG_STATIC struct input_binding input_menu_h,
31 input_menu_v,
32 input_menu_press,
33 input_menu_back,
34 input_menu_toggle,
35 input_menu_toggle_kbm;
36
37 VG_STATIC void menu_btn_quit( int event );
38 VG_STATIC void menu_btn_skater( int event );
39 VG_STATIC void menu_btn_blur( int event );
40 VG_STATIC void menu_btn_fuckoff( int event );
41 VG_STATIC void menu_btn_reset( int event );
42 VG_STATIC void menu_btn_map( int event );
43 VG_STATIC void menu_btn_settings( int event );
44
45 VG_STATIC mdl_node *menu_pnode_fov_slider,
46 *menu_pnode_fov_min,
47 *menu_pnode_fov_max,
48 *menu_pnode_vol_slider,
49 *menu_pnode_vol_min,
50 *menu_pnode_vol_max;
51
52 struct
53 {
54 /* state */
55 int loc;
56 u32 page;
57
58 /* map browser */
59 struct menu_map_file
60 {
61 char name[ 64 ];
62 }
63 maps_list[ 16 ];
64
65 int selected_map,
66 map_count;
67 }
68 VG_STATIC game_menu;
69
70 enum menu_page
71 {
72 k_menu_page_main = 0x1,
73 k_menu_page_skater = 0x2,
74 k_menu_page_quit = 0x4,
75 k_menu_page_settings = 0x8,
76 k_menu_page_map = 0x10
77 };
78
79 struct menu_btn_userdata
80 {
81 union
82 {
83 int i;
84 void *ptr_generic;
85 };
86 };
87
88 VG_STATIC int menu_settings_if( struct menu_btn_userdata ud )
89 {
90 if( game_menu.page & k_menu_page_settings )
91 {
92 int *ptr = ud.ptr_generic;
93 return *ptr;
94 }
95 else
96 return 0;
97 }
98
99 VG_STATIC int menu_vis( struct menu_btn_userdata ud )
100 {
101 if( ud.i & game_menu.page )
102 return 1;
103 else
104 return 0;
105 }
106
107 VG_STATIC int menu_controller( struct menu_btn_userdata ud )
108 {
109 if( (game_menu.page & (k_menu_page_main|k_menu_page_settings))
110 && (ud.i == menu_display_controller) )
111 return 1;
112 return 0;
113 }
114
115 VG_STATIC int menu_controller_inf( struct menu_btn_userdata ud )
116 {
117 if( (game_menu.page & k_menu_page_settings)
118 && (ud.i == menu_display_controller) )
119 return 1;
120 return 0;
121 }
122
123 struct menu_button
124 {
125 const char *name;
126
127 int (*fn_visibility)( struct menu_btn_userdata ud );
128 struct menu_btn_userdata user;
129
130 void (*fn_press)( int event );
131
132 const char *ll,
133 *lu,
134 *lr,
135 *ld;
136
137 mdl_node *pnode;
138 float falpha, fsize;
139 }
140 VG_STATIC menu_buttons[] =
141 {
142 {
143 "text_quit", menu_vis, {.i=k_menu_page_main|k_menu_page_quit},
144 .fn_press = menu_btn_quit,
145 .ld="text_reset", .lr="text_settings", .ll="text_map"
146 },
147 {
148 "text_quitty", menu_vis, {.i=k_menu_page_quit}
149 },
150 {
151 "text_yes", menu_vis, {.i=k_menu_page_quit},
152 .fn_press = menu_btn_fuckoff
153 },
154 {
155 "text_reset", menu_vis, {.i=k_menu_page_main},
156 .fn_press = menu_btn_reset,
157 .lu="text_quit", .ld="text_skater", .ll="text_map", .lr="text_settings"
158 },
159 {
160 "text_skater", menu_vis, {.i=k_menu_page_main|k_menu_page_skater},
161 .fn_press = menu_btn_skater,
162 .lu="text_reset", .ll="text_map", .lr="text_settings"
163 },
164 {
165 "text_map", menu_vis, {.i=k_menu_page_main},
166 .fn_press = menu_btn_map,
167 .lr="text_reset"
168 },
169 {
170 "text_settings", menu_vis, {.i=k_menu_page_main|k_menu_page_settings},
171 .fn_press = menu_btn_settings,
172 .ll="text_reset"
173 },
174 {
175 "skater_left", menu_vis, {k_menu_page_skater}
176 },
177 {
178 "skater_right", menu_vis, {k_menu_page_skater}
179 },
180
181 {
182 "fov_slider", menu_vis, {k_menu_page_settings},
183 .ld="text_blur"
184 },
185 { "fov_info", menu_vis, {k_menu_page_settings} },
186
187 {
188 "vol_slider", menu_vis, {k_menu_page_settings},
189 .lu="text_blur"
190 },
191 { "vol_info", menu_vis, {k_menu_page_settings} },
192
193 {
194 "text_blur", menu_vis, {k_menu_page_settings},
195 .fn_press = menu_btn_blur,
196 .lu="fov_slider", .ld="vol_slider"
197 },
198 {
199 "text_blur_check", menu_settings_if, {.ptr_generic=&cl_blur}
200 },
201
202 { "ctr_xbox", menu_controller_inf, {k_menu_controller_type_xbox}},
203 { "ctr_xbox_text", menu_controller_inf, {k_menu_controller_type_xbox}},
204 { "ctr_steam", menu_controller_inf, {k_menu_controller_type_steam}},
205 { "ctr_steam_text", menu_controller_inf, {k_menu_controller_type_steam}},
206 { "ctr_deck", menu_controller_inf, {k_menu_controller_type_steam_deck}},
207 { "ctr_deck_text", menu_controller_inf, {k_menu_controller_type_steam_deck}},
208 { "ctr_ps", menu_controller_inf, {k_menu_controller_type_playstation}},
209 { "ctr_ps_text", menu_controller_inf, {k_menu_controller_type_playstation}},
210 { "ctr_kbm", menu_controller_inf, {k_menu_controller_type_keyboard}},
211 { "ctr_kbm_text", menu_controller_inf, {k_menu_controller_type_keyboard}},
212 {
213 "text_paused", menu_vis, {k_menu_page_main}
214 },
215 };
216
217 VG_STATIC int menu_get_loc( const char *loc )
218 {
219 for( int i=0; i<vg_list_size(menu_buttons); i++ )
220 if( !strcmp( menu_buttons[i].name, loc ) )
221 return i;
222
223 assert(0);
224 return 0;
225 }
226
227 VG_STATIC void menu_btn_reset( int event )
228 {
229 reset_player(0,NULL);
230 world_routes_clear();
231
232 cl_menu_go_away = 1;
233 game_menu.page = 0;
234 }
235
236 VG_STATIC void menu_btn_fuckoff( int event )
237 {
238 vg.window_should_close = 1;
239 }
240
241 VG_STATIC void menu_btn_quit( int event )
242 {
243 game_menu.page = k_menu_page_quit;
244 game_menu.loc = menu_get_loc( "text_yes" );
245 }
246
247 VG_STATIC void menu_btn_settings( int event )
248 {
249 game_menu.page = k_menu_page_settings;
250 game_menu.loc = menu_get_loc( "fov_slider" );
251 }
252
253 VG_STATIC void menu_btn_skater( int event )
254 {
255 game_menu.page = k_menu_page_skater;
256 }
257
258 VG_STATIC void menu_btn_blur( int event )
259 {
260 cl_blur ^= 0x1;
261 }
262
263 VG_STATIC void menu_btn_map( int event )
264 {
265 game_menu.page = k_menu_page_map;
266 game_menu.map_count = 0;
267 game_menu.selected_map = 0;
268
269 tinydir_dir dir;
270 tinydir_open( &dir, "maps" );
271
272 while( dir.has_next )
273 {
274 tinydir_file file;
275 tinydir_readfile( &dir, &file );
276
277 if( file.is_reg )
278 {
279 struct menu_map_file *mf = &game_menu.maps_list[ game_menu.map_count ];
280
281 vg_strncpy( file.name, mf->name,
282 vg_list_size(game_menu.maps_list[0].name)-1 );
283
284 game_menu.map_count ++;
285 if( game_menu.map_count == vg_list_size(game_menu.maps_list) )
286 break;
287 }
288
289 tinydir_next( &dir );
290 }
291
292 tinydir_close(&dir);
293 }
294
295 VG_STATIC void menu_crap_ui(void)
296 {
297 if( cl_menu && (game_menu.page == k_menu_page_map) )
298 {
299 ui_rect box;
300 box[0] = vg.window_x/2 - 150;
301 box[1] = vg.window_y/2 - 300;
302 box[2] = 300;
303 box[3] = 600;
304
305 ui_fill_rect( box, 0xa0000000 );
306
307 if( game_menu.map_count == 0 )
308 {
309 ui_text( (ui_rect){ vg.window_x/2, box[1]+8, 0,0 }, "No maps found", 1,
310 k_text_align_center );
311 }
312 else
313 {
314 ui_rect_pad( box, 4 );
315 box[3] = 16;
316
317 for( int i=0; i<game_menu.map_count; i++ )
318 {
319 struct menu_map_file *mf = &game_menu.maps_list[ i ];
320
321 ui_fill_rect( box, game_menu.selected_map == i? 0xa0ffffff:
322 0xa0808080 );
323 ui_text( (ui_rect){ vg.window_x/2, box[1]+2, 0,0 },
324 mf->name, 1, k_text_align_center );
325 box[1] += 16+4;
326 }
327 }
328 }
329 }
330
331 VG_STATIC void steam_on_game_overlay( CallbackMsg_t *msg )
332 {
333 GameOverlayActivated_t *inf = (GameOverlayActivated_t *)msg->m_pubParam;
334 vg_info( "Steam game overlay activated; pausing\n" );
335
336 if( inf->m_bActive )
337 {
338 cl_menu = 1;
339 game_menu.page = k_menu_page_main;
340 game_menu.loc = menu_get_loc( "text_skater" );
341 }
342 }
343
344 VG_STATIC void menu_init(void)
345 {
346 vg_create_unnamed_input( &input_menu_h, k_input_type_axis );
347 vg_create_unnamed_input( &input_menu_v, k_input_type_axis );
348 vg_create_unnamed_input( &input_menu_back, k_input_type_button );
349 vg_create_unnamed_input( &input_menu_press, k_input_type_button );
350 vg_create_unnamed_input( &input_menu_toggle, k_input_type_button );
351 vg_create_unnamed_input( &input_menu_toggle_kbm, k_input_type_button );
352
353 vg_apply_bind_str( &input_menu_h, "", "gp-ls-h" );
354 vg_apply_bind_str( &input_menu_h, "+", "right" );
355 vg_apply_bind_str( &input_menu_h, "-", "left" );
356 vg_apply_bind_str( &input_menu_v, "", "-gp-ls-v" );
357 vg_apply_bind_str( &input_menu_v, "+", "up" );
358 vg_apply_bind_str( &input_menu_v, "-", "down" );
359 vg_apply_bind_str( &input_menu_press, "", "gp-a" );
360 vg_apply_bind_str( &input_menu_press, "", "\2enter" );
361 vg_apply_bind_str( &input_menu_back, "", "gp-b" );
362 vg_apply_bind_str( &input_menu_back, "", "\2escape" );
363 vg_apply_bind_str( &input_menu_toggle_kbm, "", "\2escape" );
364 vg_apply_bind_str( &input_menu_toggle, "", "\2gp-menu" );
365
366 vg_linear_clear( vg_mem.scratch );
367
368 mdl_open( &menu_model, "models/rs_menu.mdl" );
369 mdl_load_metadata( &menu_model, vg_mem.rtmemory );
370 mdl_load_mesh_data( &menu_model, vg_mem.scratch );
371 mdl_invert_uv_coordinates( &menu_model );
372 mdl_close( &menu_model );
373
374 vg_acquire_thread_sync();
375 {
376 mdl_unpack_glmesh( &menu_model, &menu_glmesh );
377 vg_tex2d_init( (vg_tex2d *[]){ &tex_menu }, 1 );
378 }
379 vg_release_thread_sync();
380
381 for( int i=0; i<vg_list_size(menu_buttons); i++ )
382 {
383 struct menu_button *btn = &menu_buttons[i];
384 btn->pnode = mdl_node_from_name( &menu_model, btn->name );
385
386 if( !btn->pnode )
387 {
388 vg_info( "info: %s\n", btn->name );
389 vg_fatal_exit_loop( "Menu programming error" );
390 }
391 }
392
393 menu_pnode_fov_max = mdl_node_from_name( &menu_model, "fov_slider_max" );
394 menu_pnode_fov_min = mdl_node_from_name( &menu_model, "fov_slider_min" );
395 menu_pnode_fov_slider = mdl_node_from_name( &menu_model, "fov_slider" );
396 menu_pnode_vol_max = mdl_node_from_name( &menu_model, "vol_slider_max" );
397 menu_pnode_vol_min = mdl_node_from_name( &menu_model, "vol_slider_min" );
398 menu_pnode_vol_slider = mdl_node_from_name( &menu_model, "vol_slider" );
399
400 shader_menu_register();
401
402 #ifdef SR_NETWORKED
403 steam_register_callback( k_iGameOverlayActivated, steam_on_game_overlay );
404 #endif
405 }
406
407 VG_STATIC void menu_run_directional(void)
408 {
409 struct menu_button *btn = &menu_buttons[ game_menu.loc ];
410
411 if( vg_input_button_down( &input_menu_press ) )
412 {
413 if( btn->fn_press )
414 {
415 audio_lock();
416 audio_play_oneshot( &audio_ui[0], 1.0f );
417 audio_unlock();
418
419 btn->fn_press( 1 );
420 return;
421 }
422 }
423
424 if( menu_input_cooldown <= 0.0f )
425 {
426 v2f dir = { input_menu_h.axis.value,
427 -input_menu_v.axis.value };
428
429 if( v2_length2( dir ) > 0.8f*0.8f )
430 {
431 const char *link = NULL;
432
433 if( fabsf(dir[0]) > fabsf(dir[1]) )
434 {
435 if( dir[0] > 0.0f ) link = btn->lr;
436 else link = btn->ll;
437 }
438 else
439 {
440 if( dir[1] > 0.0f ) link = btn->ld;
441 else link = btn->lu;
442 }
443
444 if( link )
445 {
446 game_menu.loc = menu_get_loc( link );
447 menu_input_cooldown = 0.25f;
448 }
449 }
450 }
451 }
452
453 VG_STATIC int menu_page_should_backout(void)
454 {
455 return vg_input_button_down( &input_menu_back );
456 }
457
458 VG_STATIC void menu_close(void)
459 {
460 cl_menu_go_away = 1;
461 game_menu.page = 0;
462 game_menu.loc = menu_get_loc( "text_skater" );
463 }
464
465 VG_STATIC void menu_page_main(void)
466 {
467 if( menu_page_should_backout() )
468 {
469 menu_close();
470 return;
471 }
472
473 menu_fov_target = 112.0f;
474 menu_run_directional();
475 }
476
477 VG_STATIC void menu_page_map(void)
478 {
479 if( menu_page_should_backout() )
480 {
481 game_menu.page = k_menu_page_main;
482 game_menu.loc = menu_get_loc( "text_map" );
483 }
484
485 if( game_menu.map_count > 0 )
486 {
487 float v = input_menu_v.axis.value;
488 if( (fabsf(v) > 0.7f) && (menu_input_cooldown <= 0.0f) )
489 {
490 audio_lock();
491 audio_play_oneshot( &audio_rewind[4], 1.0f );
492 audio_unlock();
493
494 if( v < 0.0f )
495 {
496 game_menu.selected_map --;
497
498 if( game_menu.selected_map < 0 )
499 game_menu.selected_map = game_menu.map_count-1;
500
501 menu_input_cooldown = 0.25f;
502 }
503 else
504 {
505 game_menu.selected_map ++;
506
507 if( game_menu.selected_map >= game_menu.map_count )
508 game_menu.selected_map = 0;
509
510 menu_input_cooldown = 0.25f;
511 }
512 }
513
514 if( vg_input_button_down( &input_menu_press ) )
515 {
516 /* load map */
517 char temp[256];
518 strcpy( temp, "maps/" );
519 strcat( temp, game_menu.maps_list[game_menu.selected_map].name );
520
521 world_change_world( 1, (const char *[]){ temp } );
522 menu_close();
523 }
524 }
525
526 menu_fov_target = 80.0f;
527 }
528
529 VG_STATIC void menu_page_quit(void)
530 {
531 if( menu_page_should_backout() )
532 {
533 game_menu.page = k_menu_page_main;
534 game_menu.loc = menu_get_loc( "text_quit" );
535 }
536
537 menu_fov_target = 90.0f;
538 menu_run_directional();
539 }
540
541 VG_STATIC void menu_page_skater(void)
542 {
543 float h = input_menu_h.axis.value;
544 menu_fov_target = 97.0f;
545
546 if( menu_page_should_backout() )
547 {
548 game_menu.page = k_menu_page_main;
549 game_menu.loc = menu_get_loc( "text_skater" );
550 return;
551 }
552
553 if( (fabsf(h) > 0.7f) && (menu_input_cooldown <= 0.0f) )
554 {
555 audio_lock();
556 audio_play_oneshot( &audio_rewind[4], 1.0f );
557 audio_unlock();
558
559 if( h < 0.0f )
560 {
561 cl_playermdl_id --;
562 if( cl_playermdl_id < 0 )
563 cl_playermdl_id = 2;
564
565 int li = menu_get_loc( "skater_left" );
566
567 menu_buttons[li].fsize = 0.4f;
568 menu_buttons[li].falpha = 1.0f;
569
570 menu_input_cooldown = 0.25f;
571 }
572 else
573 {
574 cl_playermdl_id ++;
575 if( cl_playermdl_id > 2 )
576 cl_playermdl_id = 0;
577
578 int ri = menu_get_loc( "skater_left" );
579
580 menu_buttons[ri].fsize = 0.4f;
581 menu_buttons[ri].falpha = 1.0f;
582
583 menu_input_cooldown = 0.25f;
584 }
585 }
586 }
587
588 VG_STATIC void menu_slider( float *value, float min, float max,
589 mdl_node *slider, mdl_node *pmin, mdl_node *pmax )
590 {
591 float h = input_menu_h.axis.value;
592
593 if( fabsf(h) > 0.04f )
594 *value += h * vg.frame_delta;
595 *value = vg_clampf( *value, min, max );
596
597 v3_lerp( pmin->co, pmax->co, *value, slider->co );
598 }
599
600 VG_STATIC void menu_page_settings(void)
601 {
602 menu_run_directional();
603
604 if( game_menu.loc == menu_get_loc( "fov_slider" ) )
605 {
606 menu_slider( &cl_fov, 0.0f, 1.0f,
607 menu_pnode_fov_slider, menu_pnode_fov_min,
608 menu_pnode_fov_max );
609
610 menu_fov_target = vg_lerpf( 97.0f, 135.0f, cl_fov ) * 0.8f;
611 }
612 else if( game_menu.loc == menu_get_loc( "vol_slider" ) )
613 {
614 menu_slider( &cl_volume, 0.0f, 1.0f,
615 menu_pnode_vol_slider, menu_pnode_vol_min,
616 menu_pnode_vol_max );
617 }
618
619 if( menu_page_should_backout() )
620 {
621 game_menu.page = k_menu_page_main;
622 game_menu.loc = menu_get_loc( "text_settings" );
623 return;
624 }
625 }
626
627 VG_STATIC void menu_update(void)
628 {
629 vg_input_update( 1, &input_menu_h );
630 vg_input_update( 1, &input_menu_v );
631 vg_input_update( 1, &input_menu_back );
632 vg_input_update( 1, &input_menu_press );
633 vg_input_update( 1, &input_menu_toggle );
634 vg_input_update( 1, &input_menu_toggle_kbm );
635
636 int toggle_gp = vg_input_button_down( &input_menu_toggle ),
637 toggle_kb = vg_input_button_down( &input_menu_toggle_kbm ),
638 wait_for_a_sec = 0;
639
640 if( toggle_gp || toggle_kb )
641 {
642 if( cl_menu )
643 {
644 if( toggle_gp )
645 {
646 menu_close();
647 }
648 }
649 else
650 {
651 if( toggle_kb )
652 wait_for_a_sec = 1;
653
654 cl_menu = 1;
655 game_menu.page = 1;
656 }
657 }
658
659 if( !wait_for_a_sec && cl_menu )
660 {
661 if( game_menu.page == k_menu_page_main )
662 menu_page_main();
663 else if( game_menu.page == k_menu_page_skater )
664 menu_page_skater();
665 else if( game_menu.page == k_menu_page_quit )
666 menu_page_quit();
667 else if( game_menu.page == k_menu_page_settings )
668 menu_page_settings();
669 else if( game_menu.page == k_menu_page_map )
670 menu_page_map();
671 }
672
673 struct menu_button *btn = &menu_buttons[ game_menu.loc ];
674
675 v3f pos;
676 v2f angles;
677
678 /* Base */
679 {
680 v3f lookdir;
681 v3f *mtx = player.mdl.sk.final_mtx[player.mdl.id_head];
682 m3x3_mulv( mtx, (v3f){-1.0f,0.0f,0.0f}, lookdir );
683
684 lookdir[1] = 0.0f;
685 v3_normalize( lookdir );
686
687 v3f center_rough;
688
689 if( player.is_dead )
690 {
691 v3_copy(player.mdl.ragdoll[ player.mdl.id_hip-1 ].rb.co, center_rough);
692 }
693 else
694 {
695 v3_add( player.camera_pos, player.visual_transform[3], center_rough );
696 v3_muls( center_rough, 0.5f, center_rough );
697 }
698
699 v3_muladds( center_rough, lookdir, 1.5f, pos );
700 v3_add( (v3f){ 0.0f,0.8f,0.0f}, pos, pos );
701
702 angles[1] = 0.0f;
703 angles[0] = -atan2f( lookdir[0], lookdir[2] );
704
705 /* setup model matrix */
706 v4f qmenu_mdl;
707 q_axis_angle( qmenu_mdl, (v3f){0.0f,1.0f,0.0f}, -angles[0] );
708
709 q_m3x3( qmenu_mdl, menu_mdl_mtx );
710 v3_copy( center_rough, menu_mdl_mtx[3] );
711 }
712
713 /* Extra */
714 {
715 v3f delta;
716 v3_sub( btn->pnode->co, (v3f){ 0.0f,1.5f,-1.5f }, delta );
717 v3_normalize( delta );
718
719 float y = atan2f( delta[0], delta[2] ),
720 p = -sinf(delta[1]),
721 dt = vg.frame_delta;
722
723 menu_extra_angles[0] = vg_lerpf( menu_extra_angles[0], y, dt );
724 menu_extra_angles[1] = vg_lerpf( menu_extra_angles[1], p, dt );
725
726 v2_muladds( angles, menu_extra_angles, 0.8f, angles );
727 angles[0] = fmodf( angles[0], VG_TAUf );
728 }
729
730 /* Update camera */
731 {
732 main_camera.angles[0] =
733 vg_alerpf( main_camera.angles[0], angles[0], menu_opacity );
734 main_camera.angles[1] =
735 vg_lerpf ( main_camera.angles[1], angles[1], menu_opacity );
736 v3_lerp( main_camera.pos, pos, menu_opacity, main_camera.pos );
737
738 camera_update_transform( &main_camera );
739 }
740
741 float dt = vg.frame_delta * 6.0f;
742 menu_opacity = vg_lerpf( menu_opacity, cl_menu&&!cl_menu_go_away, dt );
743
744 if( menu_opacity <= 0.01f )
745 {
746 cl_menu = 0;
747 cl_menu_go_away = 0;
748 }
749
750 vg.time_rate = 1.0-(double)menu_opacity;
751
752 if( cl_menu )
753 {
754 menu_input_cooldown -= vg.frame_delta;
755 }
756 }
757
758 /* https://iquilezles.org/articles/functions/ */
759 float expSustainedImpulse( float x, float f, float k )
760 {
761 float s = fmaxf(x-f,0.0f);
762 return fminf( x*x/(f*f), 1.0f+(2.0f/f)*s*expf(-k*s));
763 }
764
765 VG_STATIC void menu_render( camera *cam )
766 {
767 glEnable(GL_BLEND);
768 glDisable(GL_DEPTH_TEST);
769 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
770 glBlendEquation(GL_FUNC_ADD);
771
772 shader_fscolour_use();
773 shader_fscolour_uColour( (v4f){ 0.1f, 0.1f, 0.3f, menu_opacity*0.5f } );
774 render_fsquad();
775
776 glEnable( GL_DEPTH_TEST );
777 glDisable( GL_BLEND );
778
779 m4x3f mtx;
780
781 shader_menu_use();
782 shader_menu_uColour( (v4f){ 1.0f,1.0f,1.0f,1.0f} );
783 shader_menu_uTexMain( 1 );
784 vg_tex2d_bind( &tex_menu, 1 );
785
786 shader_menu_uPv( cam->mtx.pv );
787 shader_menu_uPvmPrev( cam->mtx_prev.pv );
788 mesh_bind( &menu_glmesh );
789
790 for( int i=0; i<vg_list_size(menu_buttons); i++ )
791 {
792 struct menu_button *btn = &menu_buttons[i];
793 float talpha = i==game_menu.loc? 1.0f: 0.0f,
794 tsize0 = btn->fn_visibility( btn->user )? 1.0f: 0.0f,
795 tsize1 = i==game_menu.loc? 0.07f: 0.0f,
796 tsize = tsize0+tsize1;
797
798 btn->falpha = vg_lerpf( btn->falpha, talpha, vg.frame_delta * 14.0f );
799 btn->fsize = vg_lerpf( btn->fsize, tsize, vg.frame_delta * 3.0f );
800
801 /* Colour */
802 v4f vselected = {0.95f*1.3f,0.45f*1.3f,0.095f*1.3f, 1.0f},
803 vnormal = {1.0f,1.0f,1.0f, 1.0f},
804 vcurrent;
805
806 v4_lerp( vnormal, vselected, btn->falpha, vcurrent );
807 shader_menu_uColour( vcurrent );
808
809 /* Create matrix */
810 m4x3f mtx_size;
811 mdl_node_transform( btn->pnode, mtx );
812 m4x3_mul( menu_mdl_mtx, mtx, mtx );
813 m4x3_identity( mtx_size );
814 m4x3_scale( mtx_size, expSustainedImpulse( btn->fsize, 0.5f, 8.7f) );
815 m4x3_mul( mtx, mtx_size, mtx );
816 shader_menu_uMdl( mtx );
817
818 for( int j=0; j<btn->pnode->submesh_count; j++ )
819 {
820 mdl_submesh *sm =
821 &menu_model.submesh_buffer[ btn->pnode->submesh_start+j ];
822 mdl_draw_submesh( sm );
823 }
824 }
825
826 /*
827 for( int i=0; i<menu_model->node_count; i++ )
828 {
829 mdl_node *pnode = mdl_node_from_id( menu_model, i );
830
831 for( int j=0; j<pnode->submesh_count; j++ )
832 {
833 mdl_submesh *sm =
834 mdl_submesh_from_id( menu_model, pnode->submesh_start+j );
835
836 mdl_node_transform( pnode, mtx );
837 m4x3_mul( menu_mdl_mtx, mtx, mtx );
838 shader_menu_uMdl( mtx );
839
840 mdl_draw_submesh( sm );
841 }
842 }
843 */
844 }
845
846 #endif /* MENU_H */