From: hgn Date: Mon, 6 Dec 2021 22:34:24 +0000 (+0000) Subject: leaderboard have picture X-Git-Url: https://harrygodden.com/git/?p=fishladder.git;a=commitdiff_plain;h=fa88b9de1e8416023822f91ec88971bfadcf1702 leaderboard have picture --- diff --git a/fishladder.c b/fishladder.c index 7f47fdb..3b2c09f 100644 --- a/fishladder.c +++ b/fishladder.c @@ -356,13 +356,16 @@ struct world int num_fishes; char map_name[64]; - struct career_level *ptr_career_level; + //struct career_level *ptr_career_level; + struct cmp_level *pCmpLevel; u32 score; u32 completed; u32 time; } world = {}; +void leaderboard_set_score( struct cmp_level *cmp_level, u32 score ); + static void map_free(void) { arrfree( world.data ); @@ -806,7 +809,7 @@ static int console_load_map( int argc, char const *argv[] ) if( text_source ) { vg_info( "Loading map: '%s'\n", map_path ); - world.ptr_career_level = NULL; + world.pCmpLevel = NULL; if( !map_load( text_source, argv[0] ) ) { @@ -814,17 +817,7 @@ static int console_load_map( int argc, char const *argv[] ) return 0; } - free( text_source ); - - for( int i = 0; i < vg_list_size( level_pack_1 ); i ++ ) - { - if( !strcmp( level_pack_1[i], argv[0] ) ) - { - world.ptr_career_level = career.levels + i; - break; - } - } - + free( text_source ); return 1; } else @@ -875,10 +868,13 @@ static int console_changelevel( int argc, char const *argv[] ) } void leaderboard_found( LeaderboardFindResult_t *pCallback ); +void leaderboard_downloaded( LeaderboardScoresDownloaded_t *pCallback ); + void vg_start(void) { // Steamworks callbacks sw_leaderboard_found = &leaderboard_found; + sw_leaderboard_downloaded = &leaderboard_downloaded; vg_function_push( (struct vg_cmd){ .name = "_map_write", @@ -1038,6 +1034,7 @@ void vg_start(void) void vg_free(void) { + sw_free_opengl(); console_save_map( 0, NULL ); career_serialize(); @@ -1760,11 +1757,12 @@ void vg_update(void) } // Copy into career data - if( world.ptr_career_level ) + if( world.pCmpLevel ) { - world.ptr_career_level->score = world.score; - world.ptr_career_level->time = world.time; - world.ptr_career_level->completed = world.completed; + if( world.score > world.pCmpLevel->completed_score ) + leaderboard_set_score( world.pCmpLevel, world.score ); + + world.pCmpLevel->completed_score = world.score; } simulation_stop(); // TODO: Async? @@ -2278,7 +2276,30 @@ static ui_colourset flcol_list_locked = { static struct { SteamLeaderboard_t steam_leaderboard; - int leaderboard_matches; + int leaderboard_show; + + struct leaderboard_player + { + // Internal + u64_steamid id; + i32 score; + + // To be displayed + char score_text[ 16 ]; + char player_name[ 48 ]; + GLuint texture; // Not dynamic + } + leaderboard_players[10]; + int leaderboard_count; + + struct + { + struct cmp_level *level; + + i32 score; + int is_waiting; + } + upload_request; struct cmp_level *level_selected; } @@ -2412,7 +2433,7 @@ void vg_ui(void) if( gui_button( 2 + i ) == k_button_click ) { ui_data.level_selected = &levels[i]; - ui_data.leaderboard_matches = 0; + ui_data.leaderboard_show = 0; sw_find_leaderboard( ui_data.level_selected->map_name ); } @@ -2511,8 +2532,10 @@ void vg_ui(void) if( gui_button( 3002 ) == k_button_click ) { console_changelevel( 1, &ui_data.level_selected->map_name ); + world.pCmpLevel = ui_data.level_selected; + ui_data.level_selected = NULL; - ui_data.leaderboard_matches = 0; + ui_data.leaderboard_show = 0; } gui_text( "Play", 6, 0 ); gui_end(); @@ -2522,7 +2545,7 @@ void vg_ui(void) } gui_end_right(); - if( ui_data.leaderboard_matches ) + if( ui_data.leaderboard_show ) { ui_global_ctx.cursor[0] += 16; ui_global_ctx.cursor[3] = 250; @@ -2531,21 +2554,144 @@ void vg_ui(void) gui_new_node(); { gui_fill_rect( ui_global_ctx.cursor, 0xff5a4e4d ); + + ui_rect_pad( ui_global_ctx.cursor, 8 ); + + gui_new_node(); + { + ui_global_ctx.cursor[3] = 32+8; + + for( int i = 0; i < ui_data.leaderboard_count; i ++ ) + { + gui_new_node(); + { + struct leaderboard_player *player = &ui_data.leaderboard_players[i]; + + ui_global_ctx.cursor[0] += 4; + ui_global_ctx.cursor[1] += 4; + ui_global_ctx.cursor[2] = 32; + ui_global_ctx.cursor[3] = 32; + + gui_new_node(); + { + gui_push_image( ui_global_ctx.cursor, player->texture ); + } + gui_end_right(); + + gui_text( player->player_name, 6, 0 ); + + ui_global_ctx.cursor[2] = 50; + gui_align_right(); + + gui_text( player->score_text, 6, 0 ); + } + gui_end_down(); + + ui_global_ctx.cursor[1] += 2; + + } + } + gui_end(); } gui_end(); } } } +void leaderboard_dispatch_score(void) +{ + sw_upload_leaderboard_score( + ui_data.upload_request.level->steam_leaderboard, + k_ELeaderboardUploadScoreMethodKeepBest, + ui_data.upload_request.score, + NULL, + 0 + ); + + ui_data.upload_request.is_waiting = 0; + + vg_success( "Dispatched leaderboard score\n" ); +} + void leaderboard_found( LeaderboardFindResult_t *pCallback ) { if( !pCallback->m_bLeaderboardFound ) + { vg_error( "Leaderboard could not be found\n" ); + ui_data.steam_leaderboard = 0; + } + else + { + const char *recieved_name = sw_get_leaderboard_name( pCallback->m_hSteamLeaderboard ); + + // Update UI state and request entries if this callback found the current UI level + if( ui_data.level_selected ) + { + if( !strcmp( recieved_name, ui_data.level_selected->map_name ) ) + { + sw_download_leaderboard_entries( pCallback->m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, 0, 10 ); + ui_data.level_selected->steam_leaderboard = pCallback->m_hSteamLeaderboard; + } + } + + // Dispatch the waiting request if there was one + if( ui_data.upload_request.is_waiting ) + { + if( !strcmp( recieved_name, ui_data.upload_request.level->map_name ) ) + { + ui_data.upload_request.level->steam_leaderboard = pCallback->m_hSteamLeaderboard; + leaderboard_dispatch_score(); + } + } + } +} + +void leaderboard_downloaded( LeaderboardScoresDownloaded_t *pCallback ) +{ + // Update UI if this leaderboard matches what we currently have in view + if( ui_data.level_selected->steam_leaderboard == pCallback->m_hSteamLeaderboard ) + { + vg_info( "Recieved %d entries\n", pCallback->m_cEntryCount ); + ui_data.leaderboard_count = VG_MIN( pCallback->m_cEntryCount, 10 ); + + for( int i = 0; i < ui_data.leaderboard_count; i ++ ) + { + LeaderboardEntry_t entry; + sw_get_downloaded_entry( pCallback->m_hSteamLeaderboardEntries, i, &entry, NULL, 0 ); + + struct leaderboard_player *player = &ui_data.leaderboard_players[i]; + + player->id = entry.m_steamIDUser.m_unAll64Bits; + strncpy( player->player_name, sw_get_friend_persona_name( player->id ), vg_list_size( player->player_name )-1 ); + player->score = entry.m_nScore; + + snprintf( player->score_text, vg_list_size(player->score_text), "%d", player->score ); + player->texture = sw_get_player_image( player->id ); + + if( player->texture == 0 ) + player->texture = tex_unkown.name; + } + + if( ui_data.leaderboard_count ) + ui_data.leaderboard_show = 1; + else + ui_data.leaderboard_show = 0; + } + else vg_warn( "Downloaded leaderboard does not match requested!\n" ); +} + +void leaderboard_set_score( struct cmp_level *cmp_level, u32 score ) +{ + if( ui_data.upload_request.is_waiting ) + vg_warn( "You are uploading leaderboard entries too quickly!\n" ); + + ui_data.upload_request.level = cmp_level; + ui_data.upload_request.score = score; + ui_data.upload_request.is_waiting = 1; - ui_data.steam_leaderboard = pCallback->m_hSteamLeaderboard; - ui_data.leaderboard_matches = 0; - - if( ui_data.level_selected ) - if( !strcmp( sw_get_leaderboard_name( ui_data.steam_leaderboard ), ui_data.level_selected->map_name ) ) - ui_data.leaderboard_matches = 1; + // If leaderboard ID has been downloaded already then just immediately dispatch this + if( cmp_level->steam_leaderboard ) + leaderboard_dispatch_score(); + else + sw_find_leaderboard( cmp_level->map_name ); } diff --git a/fishladder_resources.h b/fishladder_resources.h index 9a030d1..fca9152 100644 --- a/fishladder_resources.h +++ b/fishladder_resources.h @@ -7,8 +7,9 @@ vg_tex2d tex_wood = { .path = "textures/wood.qoi" }; vg_tex2d tex_background = { .path = "textures/background.qoi" }; vg_tex2d tex_ball_noise = { .path = "textures/bnoise.qoi" }; vg_tex2d tex_monofur = { .path = "textures/ascii.qoi", .flags = VG_TEXTURE_NO_MIP }; +vg_tex2d tex_unkown = { .path = "textures/unkown.qoi" }; -vg_tex2d *texture_list[] = { &tex_tile_detail, &tex_tile_data, &tex_wood, &tex_background, &tex_ball_noise, &tex_monofur }; +vg_tex2d *texture_list[] = { &tex_tile_detail, &tex_tile_data, &tex_wood, &tex_background, &tex_ball_noise, &tex_monofur, &tex_unkown }; // AUDIO // =========================================================================================================== @@ -571,6 +572,8 @@ struct cmp_level int linked_unlocks; // When unlocked, unlock this many levels additionally int serial_id; + + SteamLeaderboard_t steam_leaderboard; }; struct cmp_level cmp_levels_tutorials[] = diff --git a/textures/ascii.png b/textures/ascii.png index daa341f..78884ae 100644 Binary files a/textures/ascii.png and b/textures/ascii.png differ diff --git a/textures/unkown.png b/textures/unkown.png new file mode 100644 index 0000000..b26c041 Binary files /dev/null and b/textures/unkown.png differ diff --git a/vg/vg_console.h b/vg/vg_console.h index 48f1f2e..c10abd6 100644 --- a/vg/vg_console.h +++ b/vg/vg_console.h @@ -80,7 +80,7 @@ static void vg_console_draw( void ) for( int i = 0; i < vg_console.len; i ++ ) { - ui_text( &ui_global_ctx, vg_console.lines[ptr], vg_console.scale, 0 ); + ui_text( &ui_global_ctx, vg_console.lines[ptr], vg_console.scale*2, 0 ); ui_global_ctx.cursor[1] -= 8*vg_console.scale; ptr --; @@ -97,7 +97,7 @@ static void vg_console_draw( void ) { ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x77333333 ); - ui_text( &ui_global_ctx, vg_console.input, vg_console.scale, 0 ); + ui_text( &ui_global_ctx, vg_console.input, vg_console.scale*2, 0 ); int start = VG_MIN( vg_console.cursor_pos, vg_console.cursor_user ), end = VG_MAX( vg_console.cursor_pos, vg_console.cursor_user ); diff --git a/vg/vg_steamworks.h b/vg/vg_steamworks.h index b14e9ea..c0baba5 100644 --- a/vg/vg_steamworks.h +++ b/vg/vg_steamworks.h @@ -547,6 +547,14 @@ int SteamAPI_ISteamUserStats_SetAchievement( ISteamUserStats *self, const char ESteamAPICallFailure SteamAPI_ISteamUtils_GetAPICallFailureReason( ISteamUtils* self, SteamAPICall_t hSteamAPICall ); +// Friends +char *SteamAPI_ISteamFriends_GetPersonaName( ISteamFriends *self ); +const char *SteamAPI_ISteamFriends_GetFriendPersonaName( ISteamFriends *self, u64_steamid steamIDFriend ); +u64_steamid SteamAPI_ISteamUser_GetSteamID( ISteamUser *self ); +int SteamAPI_ISteamFriends_GetSmallFriendAvatar( ISteamFriends *self, u64_steamid steamIDFriend ); // 32x32 +int SteamAPI_ISteamUtils_GetImageSize( ISteamUtils *self, int iImage, u32 *pnWidth, u32 *pnHeight ); +int SteamAPI_ISteamUtils_GetImageRGBA( ISteamUtils *self, int iImage, u8 *pubDest, int nDestBufferSize ); + // Leaderboards SteamAPICall_t SteamAPI_ISteamUserStats_FindOrCreateLeaderboard( ISteamUserStats* self, const char * pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType ); SteamAPICall_t SteamAPI_ISteamUserStats_FindLeaderboard( ISteamUserStats* self, const char * pchLeaderboardName ); @@ -560,6 +568,18 @@ int SteamAPI_ISteamUserStats_GetDownloadedLeaderboardEntry( ISteamUserStats* sel SteamAPICall_t SteamAPI_ISteamUserStats_UploadLeaderboardScore( ISteamUserStats* self, SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, i32 nScore, const i32 * pScoreDetails, int cScoreDetailsCount ); SteamAPICall_t SteamAPI_ISteamUserStats_AttachLeaderboardUGC( ISteamUserStats* self, SteamLeaderboard_t hSteamLeaderboard, UGCHandle_t hUGC ); +#define sw_get_image_size(...) SteamAPI_ISteamUtils_GetImageSize( steam_api_classes.utils, __VA_ARGS__ ) +#define sw_get_image_rgba(...) SteamAPI_ISteamUtils_GetImageRGBA( steam_api_classes.utils, __VA_ARGS__ ) +#define sw_get_small_friend_avatar(...) SteamAPI_ISteamFriends_GetSmallFriendAvatar( steam_api_classes.friends, __VA_ARGS__ ) +#define sw_get_steamid() SteamAPI_ISteamUser_GetSteamID( steam_api_classes.friends ) +#define sw_get_friend_persona_name(...) SteamAPI_ISteamFriends_GetFriendPersonaName( steam_api_classes.friends, __VA_ARGS__ ) +#define sw_get_persona_name() SteamAPI_ISteamFriends_GetPersonaName( steam_api_classes.friends ) +#define sw_find_leaderboard(...) SteamAPI_ISteamUserStats_FindLeaderboard( steam_api_classes.stats, __VA_ARGS__ ); +#define sw_get_leaderboard_name(...) SteamAPI_ISteamUserStats_GetLeaderboardName( steam_api_classes.stats, __VA_ARGS__ ) +#define sw_download_leaderboard_entries(...) SteamAPI_ISteamUserStats_DownloadLeaderboardEntries( steam_api_classes.stats, __VA_ARGS__ ) +#define sw_get_downloaded_entry(...) SteamAPI_ISteamUserStats_GetDownloadedLeaderboardEntry( steam_api_classes.stats, __VA_ARGS__ ) +#define sw_upload_leaderboard_score(...) SteamAPI_ISteamUserStats_UploadLeaderboardScore( steam_api_classes.stats, __VA_ARGS__ ) + HSteamPipe SteamAPI_GetHSteamPipe(); HSteamUser SteamAPI_GetHSteamUser(); @@ -573,8 +593,115 @@ struct HSteamPipe pipe; + struct cached_player + { + u64_steamid id; + GLuint avatar_texture; // tex_unkown.name + + struct cached_player *l, *r; + } + cached_players[20]; + + struct cached_player *cache_head, *cache_tail; + + u32 cache_count; + } steam_api_classes; +static void _sw_cache_push( struct cached_player *player ) +{ + player->l = NULL; + player->r = steam_api_classes.cache_head; + if( steam_api_classes.cache_head ) steam_api_classes.cache_head->l = player; + if( !steam_api_classes.cache_tail ) steam_api_classes.cache_tail = player; + steam_api_classes.cache_head = player; + steam_api_classes.cache_count ++; +} + +static void _sw_cache_evict( struct cached_player *player ) +{ + if( player == steam_api_classes.cache_tail ) steam_api_classes.cache_tail = player->l; + if( player == steam_api_classes.cache_head ) steam_api_classes.cache_head = player->r; + if( player->l ) player->l->r = player->r; + if( player->r ) player->r->l = player->l; + steam_api_classes.cache_count --; +} + +static void _sw_access_cache( struct cached_player *player ) +{ + _sw_cache_evict( player ); + _sw_cache_push( player ); +} + +static GLuint sw_get_player_image( u64_steamid usr ) +{ + // Look for player in cache + for( int i = 0; i < steam_api_classes.cache_count; i ++ ) + { + struct cached_player *player = &steam_api_classes.cached_players[i]; + + if( player->id == usr ) + { + _sw_access_cache( player ); + return player->avatar_texture; + } + } + + struct cached_player *dest; + + if( steam_api_classes.cache_count == vg_list_size( steam_api_classes.cached_players ) ) + { + dest = steam_api_classes.cache_tail; + _sw_access_cache( dest ); + + // Delete previous before creating a new one + glDeleteTextures( 1, &dest->avatar_texture ); + } + else + { + dest = &steam_api_classes.cached_players[ steam_api_classes.cache_count ]; + _sw_cache_push( dest ); + } + + dest->id = usr; + dest->avatar_texture = 0; + + // Upload new image + u32 x = 32, y = 32; + int steam_image; + + steam_image = sw_get_small_friend_avatar( usr ); + if( !steam_image ) + return 0; + + if( !sw_get_image_size( steam_image, &x, &y ) ) + return 0; + + u8 * img_buf = (u8 *)malloc( x * y * 4 ); + + if( !sw_get_image_rgba(steam_image, img_buf, x * y * 4) ) + { + free( img_buf ); + return 0; + } + + glGenTextures( 1, &dest->avatar_texture ); + glBindTexture( GL_TEXTURE_2D, dest->avatar_texture ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_buf ); + glGenerateMipmap( GL_TEXTURE_2D ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + free( img_buf ); + + return dest->avatar_texture; +} + ISteamFriends *SteamAPI_SteamFriends_v017(); ISteamUser *SteamAPI_SteamUser_v021(); ISteamUserStats *SteamAPI_SteamUserStats_v012(); @@ -586,6 +713,14 @@ static void sw_exit(void) SteamAPI_Shutdown(); } +// Needs to be manually called by client unfortunately +static void sw_free_opengl(void) +{ + for( int i = 0; i < steam_api_classes.cache_count; i ++ ) + if( steam_api_classes.cached_players[i].avatar_texture ) + glDeleteTextures( 1, &steam_api_classes.cached_players[i].avatar_texture ); +} + static int sw_init(void) { #if defined(VALVE_CALLBACK_PACK_SMALL) @@ -638,6 +773,7 @@ static int sw_init(void) void (*sw_leaderboard_found)( LeaderboardFindResult_t *pCallback ); +void (*sw_leaderboard_downloaded)( LeaderboardScoresDownloaded_t *pCallback ); static void sw_event_loop(void) { @@ -674,6 +810,9 @@ static void sw_event_loop(void) case SW_CBID_LeaderboardFindResult: if( sw_leaderboard_found ) sw_leaderboard_found( (LeaderboardFindResult_t*)pTmpCallResult ); break; + case SW_CBID_LeaderboardScoresDownloaded: + if( sw_leaderboard_downloaded ) sw_leaderboard_downloaded( (LeaderboardScoresDownloaded_t*)pTmpCallResult ); + break; default:break; } } @@ -729,13 +868,3 @@ static void sw_set_achievement( const char *vg_ach_name ) vg_success( "Achievement set: '%s'\n", vg_ach_name ); } } - -static void sw_find_leaderboard( const char *name ) -{ - SteamAPI_ISteamUserStats_FindLeaderboard( steam_api_classes.stats, name ); -} - -static const char *sw_get_leaderboard_name( SteamLeaderboard_t hSteamLeaderboard ) -{ - return SteamAPI_ISteamUserStats_GetLeaderboardName( steam_api_classes.stats, hSteamLeaderboard ); -} diff --git a/vg/vg_ui.h b/vg/vg_ui.h index 0364739..3e4ae5e 100644 --- a/vg/vg_ui.h +++ b/vg/vg_ui.h @@ -39,7 +39,7 @@ SHADER_DEFINE( shader_ui, "float clip_blend = step( aWsp.x, aClip.z ) * step( aWsp.y, aClip.w ) * step( aClip.x, aWsp.x ) * step( aClip.y, aWsp.y );" "vec4 glyph = texture( uTexGlyphs, aTexCoords );" - "FragColor = aColour * vec4( 1.0, 1.0, 1.0, glyph.r * clip_blend );" + "FragColor = vec4( aColour.rgb * glyph.rgb, aColour.a*glyph.a*clip_blend );" "}" , UNIFORMS({ "uPv", "uTexGlyphs" }) @@ -120,41 +120,20 @@ struct ui_ctx ui_colourset *colours_main; ui_colourset *colours_current; + + GLuint vao; + GLuint vbo; + GLuint ebo; + + struct ui_image + { + ui_rect rc; + GLuint image; + } + images[16]; + int image_count; }; -// Shortnames -#define gui_draw(...) ui_draw( &ui_global_ctx, __VA_ARGS__) -#define gui_current(...) ui_current( &ui_global_ctx, __VA_ARGS__) -#define gui_new_node() ui_new_node( &ui_global_ctx ) -#define gui_hasmouse(...) ui_hasmouse( &ui_global_ctx, __VA_ARGS__) -#define gui_end() ui_end( &ui_global_ctx ) -#define gui_end_down() ui_end_down( &ui_global_ctx ) -#define gui_end_right() ui_end_right( &ui_global_ctx ) -#define gui_fill_y() ui_fill_y( &ui_global_ctx) -#define gui_fill_x() ui_fill_x( &ui_global_ctx) -#define gui_align_bottom() ui_align_bottom( &ui_global_ctx ) -#define gui_align_right() ui_align_right( &ui_global_ctx ) -#define gui_align_top() ui_align_top( &ui_global_ctx ) -#define gui_align_left() ui_align_left( &ui_global_ctx ) -#define gui_clamp_rect(...) ui_clamp_rect( &ui_global_ctx, __VA_ARGS__) -#define gui_group_id(...) ui_group_id( &ui_global_ctx, __VA_ARGS__) -#define gui_capture_mouse(...) ui_capture_mouse( &ui_global_ctx, __VA_ARGS__) -#define gui_set_clip(...) ui_set_clip( &ui_global_ctx, __VA_ARGS__) -#define gui_release_clip() ui_release_clip( &ui_global_ctx ) -#define gui_fill_rect_uv(...) ui_fill_rect_uv( &ui_global_ctx, __VA_ARGS__) -#define gui_fill_rect(...) ui_fill_rect( &ui_global_ctx, __VA_ARGS__) -#define gui_text(...) ui_text( &ui_global_ctx, __VA_ARGS__) -#define gui_begin(...) ui_begin( &ui_global_ctx, __VA_ARGS__) -#define gui_resolve(...) ui_resolve( &ui_global_ctx, __VA_ARGS__) -#define gui_set_mouse(...) ui_set_mouse( &ui_global_ctx, __VA_ARGS__) -#define gui_button(...) ui_button( &ui_global_ctx, __VA_ARGS__) -#define gui_window(...) ui_window( &ui_global_ctx, __VA_ARGS__) -#define gui_want_mouse() ui_want_mouse( &ui_global_ctx ) - -#define gui_scrollbar(...) ui_scrollbar( &ui_global_ctx, __VA_ARGS__) -#define gui_override_colours(...) ui_override_colours( &ui_global_ctx, __VA_ARGS__) -#define gui_reset_colours(...) ui_reset_colours( &ui_global_ctx ) - // Globals // =========================================================================================================== @@ -162,12 +141,6 @@ struct ui_ctx int ui_glyph_override = 0; ui_px ui_glyph_spacing_x = 6; GLuint ui_glyph_texture = 0; -GLuint ui_vao; -GLuint ui_vbo; -GLuint ui_ebo; - -#define UI_BUFFER_SIZE 30000 -#define UI_INDEX_SIZE 20000 ui_colourset ui_default_colours = { .main = 0xff00ff00, @@ -176,7 +149,6 @@ ui_colourset ui_default_colours = { }; ui_ctx ui_global_ctx = { .padding = 8, - .colours_current = &ui_default_colours, .colours_main = &ui_default_colours }; @@ -184,6 +156,64 @@ ui_ctx ui_global_ctx = { // Initialization // =========================================================================================================== +static void ui_reset_colours( ui_ctx *ctx ); +static void ui_init_context( ui_ctx *ctx, int index_buffer_size ) +{ + ui_reset_colours( ctx ); + + u32 vertex_buffer_size = (index_buffer_size+(index_buffer_size/2)); + + // Generate the buffer we are gonna be drawing to + { + glGenVertexArrays(1, &ctx->vao); + glGenBuffers( 1, &ctx->vbo ); + glGenBuffers( 1, &ctx->ebo ); + glBindVertexArray( ctx->vao ); + + glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo ); + + glBufferData( GL_ARRAY_BUFFER, vertex_buffer_size * sizeof( struct ui_vert ), NULL, GL_DYNAMIC_DRAW ); + glBindVertexArray( ctx->vao ); + + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, index_buffer_size * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW ); + + u32 const stride = sizeof( struct ui_vert ); + + // XY + glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, stride, (void *)offsetof( struct ui_vert, co ) ); + glEnableVertexAttribArray( 0 ); + + // UV + glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, stride, (void *)offsetof( struct ui_vert, uv ) ); + glEnableVertexAttribArray( 1 ); + + // COLOUR + glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof( struct ui_vert, colour ) ); + glEnableVertexAttribArray( 2 ); + + // CLIPPING + glVertexAttribPointer( 3, 4, GL_SHORT, GL_FALSE, stride, (void *)offsetof( struct ui_vert, clip ) ); + glEnableVertexAttribArray( 3 ); + } + + // Initialize default context + { + ctx->verts = (struct ui_vert *)malloc( vertex_buffer_size * sizeof(struct ui_vert) ); + ctx->indices = (u16*)malloc( index_buffer_size * sizeof(u16) ); + } +} + +static void ui_context_free( ui_ctx *ctx ) +{ + glDeleteVertexArrays( 1, &ctx->vao ); + glDeleteBuffers( 1, &ctx->vbo ); + glDeleteBuffers( 1, &ctx->ebo ); + + free( ctx->verts ); + free( ctx->indices ); +} + static void ui_override_font( GLuint new_tex, ui_px space_x ) { if( ui_glyph_texture ) @@ -233,47 +263,8 @@ static void ui_default_init(void) } // Setup OpenGL memory - { - SHADER_INIT( shader_ui ); - - // Generate the buffer we are gonna be drawing to - glGenVertexArrays(1, &ui_vao); - glGenBuffers( 1, &ui_vbo ); - glGenBuffers( 1, &ui_ebo ); - glBindVertexArray( ui_vao ); - - glBindBuffer( GL_ARRAY_BUFFER, ui_vbo ); - - glBufferData( GL_ARRAY_BUFFER, UI_BUFFER_SIZE * sizeof( struct ui_vert ), NULL, GL_DYNAMIC_DRAW ); - glBindVertexArray( ui_vao ); - - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ui_ebo ); - glBufferData( GL_ELEMENT_ARRAY_BUFFER, UI_INDEX_SIZE * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW ); - - u32 const stride = sizeof( struct ui_vert ); - - // XY - glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, stride, (void *)offsetof( struct ui_vert, co ) ); - glEnableVertexAttribArray( 0 ); - - // UV - glVertexAttribPointer( 1, 2, GL_UNSIGNED_BYTE, GL_FALSE, stride, (void *)offsetof( struct ui_vert, uv ) ); - glEnableVertexAttribArray( 1 ); - - // COLOUR - glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, (void *)offsetof( struct ui_vert, colour ) ); - glEnableVertexAttribArray( 2 ); - - // CLIPPING - glVertexAttribPointer( 3, 4, GL_SHORT, GL_FALSE, stride, (void *)offsetof( struct ui_vert, clip ) ); - glEnableVertexAttribArray( 3 ); - } - - // Initialize default context - { - ui_global_ctx.verts = (struct ui_vert *)malloc( UI_BUFFER_SIZE * sizeof(struct ui_vert) ); - ui_global_ctx.indices = (u16*)malloc( UI_INDEX_SIZE * sizeof(u16) ); - } + SHADER_INIT( shader_ui ); + ui_init_context( &ui_global_ctx, 20000 ); } static void ui_default_free(void) @@ -281,22 +272,24 @@ static void ui_default_free(void) if( !ui_glyph_override ) glDeleteTextures( 1, &ui_glyph_texture ); - glDeleteVertexArrays( 1, &ui_vao ); - glDeleteBuffers( 1, &ui_vbo ); - glDeleteBuffers( 1, &ui_ebo ); - - free( ui_global_ctx.verts ); - free( ui_global_ctx.indices ); + ui_context_free( &ui_global_ctx ); } +static struct ui_vert *ui_fill_rect_uv( ui_ctx *ctx, ui_rect rect, u32 colour, ui_px uv[4] ); static void ui_draw( ui_ctx *ctx, m3x3f view_override ) { - glBindVertexArray( ui_vao ); + u32 num_indices_normal = ctx->num_indices; - glBindBuffer( GL_ARRAY_BUFFER, ui_vbo ); + // Append images to back of buffer + for( int i = 0; i < ctx->image_count; i ++ ) + ui_fill_rect_uv( ctx, ctx->images[i].rc, 0xffffffff, (ui_px[4]){0,0,128,128} ); + + glBindVertexArray( ctx->vao ); + + glBindBuffer( GL_ARRAY_BUFFER, ctx->vbo ); glBufferSubData( GL_ARRAY_BUFFER, 0, ctx->num_verts * sizeof( struct ui_vert ), ctx->verts ); - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ui_ebo ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ctx->ebo ); glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, ctx->num_indices * sizeof( u16 ), ctx->indices ); glEnable(GL_BLEND); @@ -321,9 +314,16 @@ static void ui_draw( ui_ctx *ctx, m3x3f view_override ) glBindTexture( GL_TEXTURE_2D, ui_glyph_texture ); glUniform1i( SHADER_UNIFORM( shader_ui, "uTexGlyphs" ), 0 ); - glDrawElements( GL_TRIANGLES, ctx->num_indices, GL_UNSIGNED_SHORT, (void*)(0) ); + glDrawElements( GL_TRIANGLES, num_indices_normal, GL_UNSIGNED_SHORT, (void*)(0) ); - //vg_info( "Verts: %u, Indices: %u\n", ctx->num_verts, ctx->num_indices ); + // Draw image elements + for( int i = 0; i < ctx->image_count; i ++ ) + { + struct ui_image *img = &ctx->images[i]; + + glBindTexture( GL_TEXTURE_2D, img->image ); + glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)( (num_indices_normal + 6*i)*sizeof(u16) ) ); + } glDisable(GL_BLEND); } @@ -679,6 +679,8 @@ static void ui_begin( ui_ctx *ctx, ui_px res_x, ui_px res_y ) if( ctx->click_state == 0 ) ctx->capture_mouse_id = 0; + + ctx->image_count = 0; } static void ui_resolve( ui_ctx *ctx ) @@ -898,3 +900,43 @@ static void ui_reset_colours( ui_ctx *ctx ) ctx->colours_current = ctx->colours_main; ctx->override_colour = 0xffffffff; } + +static void ui_push_image( ui_ctx *ctx, ui_rect rc, GLuint image ) +{ + struct ui_image *img = &ctx->images[ ctx->image_count ++ ]; + ui_rect_copy( rc, img->rc ); + img->image = image; +} + +// Shortnames +#define gui_draw(...) ui_draw( &ui_global_ctx, __VA_ARGS__) +#define gui_current(...) ui_current( &ui_global_ctx, __VA_ARGS__) +#define gui_new_node() ui_new_node( &ui_global_ctx ) +#define gui_hasmouse(...) ui_hasmouse( &ui_global_ctx, __VA_ARGS__) +#define gui_end() ui_end( &ui_global_ctx ) +#define gui_end_down() ui_end_down( &ui_global_ctx ) +#define gui_end_right() ui_end_right( &ui_global_ctx ) +#define gui_fill_y() ui_fill_y( &ui_global_ctx) +#define gui_fill_x() ui_fill_x( &ui_global_ctx) +#define gui_align_bottom() ui_align_bottom( &ui_global_ctx ) +#define gui_align_right() ui_align_right( &ui_global_ctx ) +#define gui_align_top() ui_align_top( &ui_global_ctx ) +#define gui_align_left() ui_align_left( &ui_global_ctx ) +#define gui_clamp_rect(...) ui_clamp_rect( &ui_global_ctx, __VA_ARGS__) +#define gui_group_id(...) ui_group_id( &ui_global_ctx, __VA_ARGS__) +#define gui_capture_mouse(...) ui_capture_mouse( &ui_global_ctx, __VA_ARGS__) +#define gui_set_clip(...) ui_set_clip( &ui_global_ctx, __VA_ARGS__) +#define gui_release_clip() ui_release_clip( &ui_global_ctx ) +#define gui_fill_rect_uv(...) ui_fill_rect_uv( &ui_global_ctx, __VA_ARGS__) +#define gui_fill_rect(...) ui_fill_rect( &ui_global_ctx, __VA_ARGS__) +#define gui_text(...) ui_text( &ui_global_ctx, __VA_ARGS__) +#define gui_begin(...) ui_begin( &ui_global_ctx, __VA_ARGS__) +#define gui_resolve(...) ui_resolve( &ui_global_ctx, __VA_ARGS__) +#define gui_set_mouse(...) ui_set_mouse( &ui_global_ctx, __VA_ARGS__) +#define gui_button(...) ui_button( &ui_global_ctx, __VA_ARGS__) +#define gui_window(...) ui_window( &ui_global_ctx, __VA_ARGS__) +#define gui_want_mouse() ui_want_mouse( &ui_global_ctx ) +#define gui_push_image(...) ui_push_image( &ui_global_ctx, __VA_ARGS__ ) +#define gui_scrollbar(...) ui_scrollbar( &ui_global_ctx, __VA_ARGS__) +#define gui_override_colours(...) ui_override_colours( &ui_global_ctx, __VA_ARGS__) +#define gui_reset_colours(...) ui_reset_colours( &ui_global_ctx )