8 #include "ent_miniworld.h"
12 #include "shaders/model_menu.h"
14 struct global_menu menu
= { .skip_starter
= 0 };
17 * Attaches memory locations to the various items in the menu
21 /* link data locations */
22 for( u32 i
=0; i
<mdl_arrcount(&menu
.items
); i
++ ){
23 ent_menuitem
*item
= mdl_arritm( &menu
.items
, i
);
25 if( item
->type
== k_ent_menuitem_type_toggle
||
26 item
->type
== k_ent_menuitem_type_slider
){
30 if( item
->type
== k_ent_menuitem_type_slider
)
31 name
= mdl_pstr( &menu
.model
, item
->slider
.pstr_data
);
33 name
= mdl_pstr( &menu
.model
, item
->checkmark
.pstr_data
);
34 vg_var
*var
= vg_console_match_var( name
);
37 if( ( item
->type
== k_ent_menuitem_type_slider
&&
38 var
->data_type
!= k_var_dtype_f32
40 ( item
->type
== k_ent_menuitem_type_toggle
&&!
41 ( var
->data_type
== k_var_dtype_i32
||
42 var
->data_type
== k_var_dtype_u32
46 vg_error( "Cannot hook to data %s(%p), because it is type %d.\n",
47 name
, var
, var
->data_type
);
51 item
->pvoid
= var
->data
;
55 vg_error( "No data named %s\n", name
);
64 /* link controllers */
68 menu
.ctr_steam
= NULL
;
71 for( u32 i
=0; i
<mdl_arrcount(&menu
.items
); i
++ ){
72 ent_menuitem
*item
= mdl_arritm( &menu
.items
, i
);
74 if( MDL_CONST_PSTREQ( &menu
.model
, item
->visual
.pstr_name
, "deck" ) )
76 if( MDL_CONST_PSTREQ( &menu
.model
, item
->visual
.pstr_name
, "kbm" ) )
78 if( MDL_CONST_PSTREQ( &menu
.model
, item
->visual
.pstr_name
, "ps" ) )
80 if( MDL_CONST_PSTREQ( &menu
.model
, item
->visual
.pstr_name
, "steam" ) )
81 menu
.ctr_steam
= item
;
82 if( MDL_CONST_PSTREQ( &menu
.model
, item
->visual
.pstr_name
, "xbox" ) )
89 skaterift
.activity
= k_skaterift_default
;
91 menu
.page
= 0xffffffff;
92 srinput
.state
= k_input_state_resume
;
97 void *alloc
= vg_mem
.rtmemory
;
99 mdl_open( &menu
.model
, "models/rs_menu.mdl", alloc
);
100 mdl_load_metadata_block( &menu
.model
, alloc
);
102 vg_linear_clear( vg_mem
.scratch
);
104 MDL_LOAD_ARRAY( &menu
.model
, &menu
.items
, ent_menuitem
, alloc
);
105 MDL_LOAD_ARRAY( &menu
.model
, &menu
.markers
, ent_marker
, alloc
);
106 MDL_LOAD_ARRAY( &menu
.model
, &menu
.cameras
, ent_camera
, alloc
);
108 u32 count
= mdl_arrcount( &menu
.model
.textures
);
109 menu
.textures
= vg_linear_alloc(alloc
,vg_align8(sizeof(GLuint
)*(count
+1)));
110 menu
.textures
[0] = vg
.tex_missing
;
112 mdl_async_load_glmesh( &menu
.model
, &menu
.mesh
, NULL
);
114 for( u32 i
=0; i
<count
; i
++ ){
115 vg_linear_clear( vg_mem
.scratch
);
116 menu
.textures
[i
+1] = vg
.tex_missing
;
118 mdl_texture
*tex
= mdl_arritm( &menu
.model
.textures
, i
);
119 void *data
= vg_linear_alloc( vg_mem
.scratch
, tex
->file
.pack_size
);
120 mdl_fread_pack_file( &menu
.model
, &tex
->file
, data
);
121 vg_tex2d_load_qoi_async( data
, tex
->file
.pack_size
,
122 VG_TEX2D_LINEAR
|VG_TEX2D_CLAMP
,
123 &menu
.textures
[i
+1] );
126 mdl_close( &menu
.model
);
128 vg_console_reg_var( "skip_starter_menu", &menu
.skip_starter
,
129 k_var_dtype_i32
, VG_VAR_PERSISTENT
);
132 void menu_at_begin(void)
134 if( menu
.skip_starter
) return;
136 skaterift
.activity
= k_skaterift_menu
;
137 menu
.page
= 0xffffffff;
138 menu_open_page( "Starter", k_ent_menuitem_stack_append
);
142 * Drop back a page until we're at the bottom which then we jus quit
144 static void menu_back_page(void){
146 if( menu
.page_depth
== 0 ){
150 menu
.page
= menu
.page_stack
[ menu
.page_depth
].page
;
151 menu
.cam
= menu
.page_stack
[ menu
.page_depth
].cam
;
153 if( menu
.input_mode
== k_menu_input_mode_keys
)
154 menu
.loc
= menu
.page_stack
[ menu
.page_depth
].loc
;
155 else menu
.loc
= NULL
;
160 * Open page to the string identifier
162 void menu_open_page( const char *name
,
163 enum ent_menuitem_stack_behaviour stackmode
)
165 srinput
.state
= k_input_state_resume
;
166 if( stackmode
== k_ent_menuitem_stack_append
){
167 if( menu
.page_depth
>= MENU_STACK_SIZE
)
168 vg_fatal_error( "Stack overflow\n" );
171 if( menu
.page_depth
== 0 )
172 vg_fatal_error( "Stack underflow\n" );
175 u32 hash
= vg_strdjb2( name
);
176 for( u32 i
=0; i
<mdl_arrcount(&menu
.items
); i
++ ){
177 ent_menuitem
*item
= mdl_arritm( &menu
.items
, i
);
179 if( item
->type
== k_ent_menuitem_type_page
){
180 if( mdl_pstreq( &menu
.model
, item
->page
.pstr_name
, name
, hash
) ){
181 u32 new_page
= __builtin_ctz( item
->groups
);
183 if( new_page
== menu
.page
){
184 if( stackmode
!= k_ent_menuitem_stack_replace
)
188 menu
.page_stack
[ menu
.page_depth
].page
= menu
.page
;
189 menu
.page_stack
[ menu
.page_depth
].cam
= menu
.cam
;
190 menu
.page_stack
[ menu
.page_depth
].loc
= menu
.loc
;
192 if( stackmode
== k_ent_menuitem_stack_append
)
195 menu
.page
= __builtin_ctz( item
->groups
);
197 if( menu
.input_mode
== k_menu_input_mode_keys
){
198 if( item
->page
.id_entrypoint
){
199 u32 id
= mdl_entity_id_id( item
->page
.id_entrypoint
);
200 menu
.loc
= mdl_arritm( &menu
.items
, id
);
204 if( item
->page
.id_viewpoint
){
205 u32 id
= mdl_entity_id_id( item
->page
.id_viewpoint
);
206 menu
.cam
= mdl_arritm( &menu
.cameras
, id
);
216 * activate a pressable type
218 static void menu_trigger_item( ent_menuitem
*item
)
221 audio_oneshot( &audio_ui
[0], 1.0f
, 0.0f
);
224 if ( item
->type
== k_ent_menuitem_type_event_button
)
226 u32 q
= item
->button
.pstr
;
228 if( MDL_CONST_PSTREQ( &menu
.model
, q
, "quit" ) )
230 vg
.window_should_close
= 1;
232 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "map" ) ){
236 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "hub" ) ){
237 if( world_static
.active_instance
== k_world_purpose_client
){
239 ent_miniworld_goback();
242 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "credits" ) ){
243 menu
.credits_open
= 1;
245 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "workshop" ) ){
246 workshop_submit_command(0,NULL
);
248 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "engine" ) ){
251 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "prem_store" ) ){
253 SteamAPI_ISteamFriends_ActivateGameOverlayToStore(
254 SteamAPI_SteamFriends(), 2103940, k_EOverlayToStoreFlag_None
);
256 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "prem_nevermind" ) ){
259 else if( MDL_CONST_PSTREQ( &menu
.model
, q
, "starter_enter" ) )
261 if( network_client
.auto_connect
)
262 network_client
.user_intent
= k_server_intent_online
;
267 else if( item
->type
== k_ent_menuitem_type_page_button
)
269 menu_open_page( mdl_pstr( &menu
.model
, item
->button
.pstr
),
270 item
->button
.stack_behaviour
);
272 else if( item
->type
== k_ent_menuitem_type_toggle
)
275 *item
->pi32
= *item
->pi32
^ 0x1;
280 static f32
menu_slider_snap( f32 value
, f32 old
, f32 notch
){
281 f32
const k_epsilon
= 0.0125f
;
283 if( fabsf(notch
-value
) < k_epsilon
){
284 if( fabsf(notch
-old
) > k_epsilon
){
286 audio_oneshot( &audio_ui
[0], 1.0f
, 0.0f
);
296 static void menu_setitem_type( ent_menuitem
*item
,
297 enum ent_menuitem_type type
){
303 * Run from vg_gui every frame
305 void menu_update(void)
307 static f32 repeater
= 0.0f
;
308 if( repeater
> 0.0f
)
309 repeater
-= vg
.time_frame_delta
;
311 if( workshop_form
.page
!= k_workshop_form_hidden
){
317 if( menu
.credits_open
|| vg
.settings_open
)
319 vg_exec_input_program( k_vg_input_type_button_u8
,
320 input_button_list
[k_srbind_mback
], &escape
);
323 menu
.credits_open
= 0;
325 if( vg
.settings_open
)
328 srinput
.state
= k_input_state_resume
;
332 escape
= button_down( k_srbind_mback
);
334 if( button_down( k_srbind_mopen
) ){
335 if( skaterift
.activity
== k_skaterift_default
){
336 skaterift
.activity
= k_skaterift_menu
;
337 menu
.page
= 0xffffffff;
338 menu_open_page( "Main Menu", k_ent_menuitem_stack_append
);
343 if( skaterift
.activity
!= k_skaterift_menu
) return;
344 enum menu_input_mode prev_mode
= menu
.input_mode
;
346 /* get buttons inputs
347 * -------------------------------------------------------------------*/
348 int ml
= button_down( k_srbind_mleft
),
349 mr
= button_down( k_srbind_mright
),
350 mu
= button_down( k_srbind_mup
),
351 md
= button_down( k_srbind_mdown
),
354 enter
= button_down( k_srbind_maccept
);
357 menu
.input_mode
= k_menu_input_mode_keys
;
361 * --------------------------------------------------------------------*/
362 menu
.mouse_dist
+= v2_length( vg
.mouse_delta
); /* TODO: Move to UI */
363 menu
.mouse_track
+= vg
.time_frame_delta
;
364 if( menu
.mouse_track
> 0.1f
){
365 menu
.mouse_track
= fmodf( menu
.mouse_track
, 0.1f
);
366 if( menu
.mouse_dist
> 10.0f
){
367 menu
.input_mode
= k_menu_input_mode_mouse
;
368 menu
.mouse_dist
= 0.0f
;
372 if( ui_clicking(UI_MOUSE_LEFT
) || ui_clicking(UI_MOUSE_RIGHT
) ){
373 menu
.input_mode
= k_menu_input_mode_mouse
;
376 if( menu
.input_mode
== k_menu_input_mode_mouse
){
379 * ------------------------------------------------------------*/
380 vg_ui
.wants_mouse
= 1;
383 * this raycasting is super cumbersome because all the functions were
384 * designed for other purposes. we dont care though.
387 m4x4_inv( menu
.view
.mtx
.p
, inverse
);
389 coords
[0] = vg_ui
.mouse
[0];
390 coords
[1] = vg
.window_y
- vg_ui
.mouse
[1];
391 v2_div( coords
, (v2f
){ vg
.window_x
, vg
.window_y
}, coords
);
392 v2_muls( coords
, 2.0f
, coords
);
393 v2_add( coords
, (v2f
){-1.0f
,-1.0f
}, coords
);
396 m4x4_mulv( inverse
, coords
, coords
);
398 m3x3_mulv( menu
.view
.transform
, coords
, ray
);
401 if( menu
.loc
&& (menu
.loc
->type
== k_ent_menuitem_type_slider
) &&
402 ui_clicking(UI_MOUSE_LEFT
) && menu
.loc
->pf32
){
404 u32 il
= mdl_entity_id_id( menu
.loc
->slider
.id_min
),
405 ir
= mdl_entity_id_id( menu
.loc
->slider
.id_max
);
406 ent_marker
*ml
= mdl_arritm( &menu
.markers
, il
),
407 *mr
= mdl_arritm( &menu
.markers
, ir
);
410 v3_muladds( menu
.view
.pos
, ray
, 100.0f
, q2
);
415 v3_sub( mr
->transform
.co
, ml
->transform
.co
, v0
);
416 v3_muladds( ml
->transform
.co
, v0
, -1.0f
, p1
);
417 v3_muladds( mr
->transform
.co
, v0
, 1.0f
, q1
);
418 closest_segment_segment( p1
, q1
, menu
.view
.pos
, q2
, &s
,&t
, c1
,c2
);
423 if( ui_click_down(UI_MOUSE_LEFT
) ){
424 menu
.slider_offset
= *menu
.loc
->pf32
- s
;
427 f32 newvalue
= vg_clampf( s
+menu
.slider_offset
, 0.0f
, 1.0f
);
429 newvalue
= menu_slider_snap( newvalue
, *menu
.loc
->pf32
, 0.00f
);
430 newvalue
= menu_slider_snap( newvalue
, *menu
.loc
->pf32
, 1.00f
);
431 newvalue
= menu_slider_snap( newvalue
, *menu
.loc
->pf32
, 0.25f
);
432 newvalue
= menu_slider_snap( newvalue
, *menu
.loc
->pf32
, 0.50f
);
433 newvalue
= menu_slider_snap( newvalue
, *menu
.loc
->pf32
, 0.75f
);
435 *menu
.loc
->pf32
= newvalue
;
439 ent_menuitem
*hit_item
= NULL
;
441 for( u32 i
=0; i
<mdl_arrcount(&menu
.items
); i
++ ){
442 ent_menuitem
*item
= mdl_arritm( &menu
.items
, i
);
444 if( item
->type
== k_ent_menuitem_type_page
) continue;
445 if( (item
->type
== k_ent_menuitem_type_visual
) ||
446 (item
->type
== k_ent_menuitem_type_visual_nocol
) ) continue;
447 if( item
->type
== k_ent_menuitem_type_binding
) continue;
448 if( !(item
->groups
& (0x1<<menu
.page
)) ) continue;
450 ent_menuitem
*ray_item
= item
;
452 if( item
->type
== k_ent_menuitem_type_slider
){
453 u32 subtarget
= mdl_entity_id_id( item
->slider
.id_handle
);
454 ray_item
= mdl_arritm( &menu
.items
, subtarget
);
461 mdl_transform_m4x3( &ray_item
->transform
, inverse_mtx
);
462 m4x3_invert_full( inverse_mtx
, inverse_mtx
);
464 m4x3_mulv( inverse_mtx
, menu
.view
.transform
[3], local_co
);
465 m3x3_mulv( inverse_mtx
, ray
, local_ray
);
466 v3_normalize( local_ray
);
468 local_ray
[0] = 1.0f
/local_ray
[0];
469 local_ray
[1] = 1.0f
/local_ray
[1];
470 local_ray
[2] = 1.0f
/local_ray
[2];
472 for( u32 j
=0; j
<ray_item
->submesh_count
; j
++ ){
473 mdl_submesh
*sm
= mdl_arritm( &menu
.model
.submeshs
,
474 ray_item
->submesh_start
+ j
);
475 if( ray_aabb1( sm
->bbx
, local_co
, local_ray
, 1000.0f
) ){
482 if( hit_item
!= menu
.loc
){
490 if( ui_click_down( UI_MOUSE_LEFT
) )
492 menu_trigger_item( menu
.loc
);
496 else if( menu
.input_mode
== k_menu_input_mode_keys
){
498 * handle button input
499 * ------------------------------------------------------------*/
500 if( (prev_mode
!= k_menu_input_mode_keys
) && !menu
.loc
){
501 for( u32 i
=0; i
<mdl_arrcount(&menu
.items
); i
++ ){
502 ent_menuitem
*item
= mdl_arritm( &menu
.items
, i
);
504 if( (item
->type
!= k_ent_menuitem_type_page
) &&
505 (item
->type
!= k_ent_menuitem_type_visual
) &&
506 (item
->type
!= k_ent_menuitem_type_visual_nocol
) &&
507 (item
->groups
& (0x1<<menu
.page
)) ){
513 if( !menu
.loc
) vg_fatal_error( "No location\n" );
515 if( menu
.loc
->type
== k_ent_menuitem_type_slider
&& menu
.loc
->pf32
){
518 if( vg_input
.display_input_method
== k_input_method_controller
){
519 move
+= button_press( k_srbind_mright
);
520 move
-= button_press( k_srbind_mleft
);
523 move
+= axis_state( k_sraxis_mbrowse_h
);
526 move
*= vg
.time_frame_delta
;
527 *menu
.loc
->pf32
= vg_clampf( *menu
.loc
->pf32
+ move
, 0.0f
, 1.0f
);
536 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
541 menu_trigger_item( menu
.loc
);
547 ent_menuitem
*nextpos
= NULL
;
551 mdl_transform_vector( &menu
.cam
->transform
, opt
, opt
);
553 for( u32 i
=0; i
<4; i
++ ){
554 u32 id
= menu
.loc
->id_links
[i
];
556 u32 index
= mdl_entity_id_id( id
);
558 ent_menuitem
*other
= mdl_arritm( &menu
.items
, index
);
560 v3_sub( menu
.loc
->transform
.co
, other
->transform
.co
, delta
);
561 v3_normalize( delta
);
563 f32 score
= v3_dot( delta
, opt
);
570 if( nextpos
&& (repeater
<= 0.0f
) )
574 audio_oneshot( &audio_ui
[3], 1.0f
, 0.0f
);
581 menu_setitem_type( menu
.ctr_deck
, k_ent_menuitem_type_disabled
);
582 menu_setitem_type( menu
.ctr_ps
, k_ent_menuitem_type_disabled
);
583 menu_setitem_type( menu
.ctr_kbm
, k_ent_menuitem_type_disabled
);
584 menu_setitem_type( menu
.ctr_xbox
, k_ent_menuitem_type_disabled
);
585 menu_setitem_type( menu
.ctr_steam
, k_ent_menuitem_type_disabled
);
587 if( vg_input
.display_input_method
== k_input_method_kbm
)
588 menu_setitem_type( menu
.ctr_kbm
, k_ent_menuitem_type_visual_nocol
);
590 if( vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS3
||
591 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS4
||
592 vg_input
.display_input_type
== SDL_CONTROLLER_TYPE_PS5
){
593 menu_setitem_type( menu
.ctr_ps
, k_ent_menuitem_type_visual_nocol
);
596 menu_setitem_type( menu
.ctr_xbox
, k_ent_menuitem_type_visual_nocol
);
598 /* FIXME: Steam/Deck controller detection? */
602 static void menu_binding_string( char buf
[128], u32 pstr
);
605 * Run from vg_gui when active
607 void menu_render(void)
610 glDisable(GL_DEPTH_TEST
);
611 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
612 glBlendEquation(GL_FUNC_ADD
);
614 shader_blitcolour_use();
616 ui_hex_to_norm( ui_colour( k_ui_bg
+3 ), colour
);
619 shader_blitcolour_uColour( colour
);
622 if( (workshop_form
.page
!= k_workshop_form_hidden
) ||
623 (vg_ui
.focused_control_type
!= k_ui_control_none
) ){
627 if( vg
.settings_open
)
630 if( menu
.credits_open
){
631 ui_rect panel
= { 0,0, 460, 400 },
632 screen
= { 0,0, vg
.window_x
,vg
.window_y
};
633 ui_rect_center( screen
, panel
);
634 ui_fill( panel
, ui_colour(k_ui_bg
) );
635 ui_outline( panel
, 1, ui_colour(k_ui_fg
), 0 );
636 ui_rect_pad( panel
, (ui_px
[]){8,8} );
639 ui_split( panel
, k_ui_axis_h
, 28*2, 0, title
, panel
);
640 ui_text( title
, "Skate Rift - Credits", 2, k_ui_align_middle_center
, 0 );
641 ui_split( panel
, k_ui_axis_h
, 28, 0, title
, panel
);
642 ui_text( title
, "Mt.Zero Software", 1, k_ui_align_middle_center
, 0 );
644 ui_split( panel
, k_ui_axis_h
, 8, 0, title
, panel
);
645 ui_split( panel
, k_ui_axis_h
, 28*2, 0, title
, panel
);
646 ui_text( title
, "Free Software", 2, k_ui_align_middle_center
, 0 );
648 ui_split( panel
, k_ui_axis_h
, 8, 0, title
, panel
);
650 "Sam Lantinga - SDL2 - libsdl.org\n"
651 "Hunter WB - Anyascii\n"
652 "David Herberth - GLAD\n"
653 "Dominic Szablewski - QOI - qoiformat.org\n"
654 "Sean Barrett - stb_image,stb_vorbis,stb_include\n"
655 "Khronos Group - OpenGL\n"
656 , 1, k_ui_align_left
, 0 );
660 glEnable( GL_DEPTH_TEST
);
661 glDisable( GL_BLEND
);
663 f32 rate
= vg
.time_frame_delta
* 12.0f
;
668 target
.fov
= menu
.cam
->fov
;
669 v3_copy( menu
.cam
->transform
.co
, target
.pos
);
672 mdl_transform_vector( &menu
.cam
->transform
, (v3f
){0.0f
,-1.0f
,0.0f
}, v0
);
673 v3_angles( v0
, target
.angles
);
675 vg_camera_lerp( &menu
.view
, &target
, rate
, &menu
.view
);
677 menu
.view
.farz
= 150.0f
;
678 menu
.view
.nearz
= 0.01f
;
680 vg_camera_update_transform( &menu
.view
);
681 vg_camera_update_view( &menu
.view
);
682 vg_camera_update_projection( &menu
.view
);
683 vg_camera_finalize( &menu
.view
);
687 shader_model_menu_use();
688 shader_model_menu_uTexMain( 1 );
689 shader_model_menu_uPv( menu
.view
.mtx
.pv
);
690 shader_model_menu_uPvmPrev( menu
.view
.mtx_prev
.pv
);
692 mesh_bind( &menu
.mesh
);
696 ui_hex_to_norm( ui_colour( k_ui_fg
), white
);
697 ui_hex_to_norm( ui_colour( k_ui_orange
+k_ui_brighter
), blue
);
699 ent_menuitem
*text_list
[ 8 ];
702 u32 current_mat
= 0xffffffff;
704 for( u32 i
=0; i
<mdl_arrcount(&menu
.items
); i
++ ){
705 ent_menuitem
*item
= mdl_arritm( &menu
.items
, i
);
707 if( item
->type
== k_ent_menuitem_type_disabled
) continue;
708 if( item
->type
== k_ent_menuitem_type_page
) continue;
709 if( !(item
->groups
& (0x1 << menu
.page
)) ) continue;
711 if( item
->type
== k_ent_menuitem_type_binding
){
712 if( text_count
< vg_list_size(text_list
) )
713 text_list
[ text_count
++ ] = item
;
715 vg_fatal_error( "Text list overflow" );
723 if( menu
.loc
->type
== k_ent_menuitem_type_slider
){
724 u32 subid
= menu
.loc
->slider
.id_handle
;
725 if( item
== mdl_arritm( &menu
.items
, mdl_entity_id_id(subid
) ))
729 if( item
== menu
.loc
)
734 if( item
->type
== k_ent_menuitem_type_visual_nocol
){
735 shader_model_menu_uColour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
739 item
->factive
= vg_lerpf( item
->factive
, selected
, rate
);
740 v4_lerp( white
, blue
, item
->factive
, colour
);
741 shader_model_menu_uColour( colour
);
744 f32 scale
= 1.0f
+item
->factive
*0.1f
;
747 mdl_transform transform
= item
->transform
;
748 v3_muls( transform
.s
, scale
, transform
.s
);
749 mdl_transform_m4x3( &transform
, mmdl
);
751 if( item
->type
== k_ent_menuitem_type_toggle
&& item
->pi32
){
752 u32 subid
= mdl_entity_id_id( item
->checkmark
.id_check
);
753 ent_menuitem
*subitem
= mdl_arritm( &menu
.items
, subid
);
755 v3_muladds( item
->transform
.co
, item
->checkmark
.offset
, scale
,
756 subitem
->transform
.co
);
758 subitem
->fvisible
= vg_lerpf( subitem
->fvisible
, *item
->pi32
, rate
);
759 v3_fill( subitem
->transform
.s
, subitem
->fvisible
);
761 else if( item
->type
== k_ent_menuitem_type_slider
&& item
->pf32
){
762 u32 il
= mdl_entity_id_id( item
->slider
.id_min
),
763 ir
= mdl_entity_id_id( item
->slider
.id_max
),
764 ih
= mdl_entity_id_id( item
->slider
.id_handle
);
765 ent_marker
*ml
= mdl_arritm( &menu
.markers
, il
),
766 *mr
= mdl_arritm( &menu
.markers
, ir
);
767 ent_menuitem
*handle
= mdl_arritm( &menu
.items
, ih
);
769 v3_lerp( ml
->transform
.co
, mr
->transform
.co
, *item
->pf32
,
770 handle
->transform
.co
);
773 shader_model_menu_uMdl( mmdl
);
775 for( u32 j
=0; j
<item
->submesh_count
; j
++ )
777 u32 index
= item
->submesh_start
+ j
;
778 mdl_submesh
*sm
= mdl_arritm( &menu
.model
.submeshs
, index
);
780 if( sm
->material_id
!= current_mat
)
782 mdl_material
*mat
= mdl_arritm( &menu
.model
.materials
,
784 glActiveTexture( GL_TEXTURE1
);
786 if( mat
->shader
== k_shader_standard
)
788 struct shader_props_standard
*props
= mat
->props
.compiled
;
790 /* FIXME: why does menu have its own texture array?? */
791 glBindTexture( GL_TEXTURE_2D
,
792 menu
.textures
[ props
->tex_diffuse
] );
796 glBindTexture( GL_TEXTURE_2D
, vg
.tex_missing
);
799 current_mat
= sm
->material_id
;
802 mdl_draw_submesh( sm
);
806 if( !text_count
) return;
811 m4x3_identity( local
);
813 font3d_bind( &gui
.font
, k_font_shader_default
, 0, NULL
, &menu
.view
);
814 for( u32 i
=0; i
<text_count
; i
++ ){
815 ent_menuitem
*item
= text_list
[ i
];
817 mdl_transform_m4x3( &item
->transform
, transform
);
819 u32 variant
= item
->binding
.font_variant
;
820 menu_binding_string( buf
, item
->binding
.pstr_bind
);
821 f32 offset
= font3d_string_width( variant
, buf
);
823 local
[3][0] = -0.5f
* offset
;
824 m4x3_mul( transform
, local
, transform
);
826 font3d_simple_draw( variant
, buf
, &menu
.view
, transform
);
830 static void menu_binding_string( char buf
[128], u32 pstr
){
832 vg_strnull( &str
, buf
, 128 );
834 if( MDL_CONST_PSTREQ( &menu
.model
, pstr
, "bind_jump" ) ){
835 vg_input_string( &str
, input_button_list
[k_srbind_jump
], 1 );
837 else if( MDL_CONST_PSTREQ( &menu
.model
, pstr
, "bind_trick0" ) ){
838 vg_strcat( &str
, "SHUVIT " );
839 vg_input_string( &str
, input_button_list
[k_srbind_trick0
], 1 );
841 else if( MDL_CONST_PSTREQ( &menu
.model
, pstr
, "bind_trick1" ) ){
842 vg_strcat( &str
, "KICKFLIP " );
843 vg_input_string( &str
, input_button_list
[k_srbind_trick1
], 1 );
845 else if( MDL_CONST_PSTREQ( &menu
.model
, pstr
, "bind_trick2" ) ){
846 vg_strcat( &str
, "TREFLIP " );
847 vg_input_string( &str
, input_button_list
[k_srbind_trick2
], 1 );
849 else if( MDL_CONST_PSTREQ( &menu
.model
, pstr
, "bind_grab" ) ){
850 vg_input_string( &str
, input_axis_list
[k_sraxis_grab
], 1 );
852 else if( MDL_CONST_PSTREQ( &menu
.model
, pstr
, "bind_grab_mod" ) ){
853 vg_input_string( &str
, input_joy_list
[k_srjoystick_grab
], 1 );
856 vg_strcat( &str
, "error" );