diff --git a/config.def.h b/config.def.h index 4dbbefe3a2..e3c31674ad 100644 --- a/config.def.h +++ b/config.def.h @@ -398,7 +398,7 @@ static bool rgui_full_width_layout = true; static unsigned rgui_aspect = RGUI_ASPECT_RATIO_4_3; static unsigned rgui_aspect_lock = RGUI_ASPECT_RATIO_LOCK_NONE; static bool rgui_shadows = false; -static bool rgui_snow = false; +static unsigned rgui_particle_effect = RGUI_PARTICLE_EFFECT_NONE; static bool rgui_extended_ascii = false; #else diff --git a/configuration.c b/configuration.c index 71f92e3372..ff68547420 100644 --- a/configuration.c +++ b/configuration.c @@ -1519,7 +1519,6 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("rgui_border_filler_thickness_enable", &settings->bools.menu_rgui_border_filler_thickness_enable, true, true, false); SETTING_BOOL("rgui_border_filler_enable", &settings->bools.menu_rgui_border_filler_enable, true, true, false); SETTING_BOOL("menu_rgui_shadows", &settings->bools.menu_rgui_shadows, true, rgui_shadows, false); - SETTING_BOOL("menu_rgui_snow", &settings->bools.menu_rgui_snow, true, rgui_snow, false); SETTING_BOOL("menu_rgui_full_width_layout", &settings->bools.menu_rgui_full_width_layout, true, rgui_full_width_layout, false); SETTING_BOOL("rgui_inline_thumbnails", &settings->bools.menu_rgui_inline_thumbnails, true, rgui_inline_thumbnails, false); SETTING_BOOL("rgui_swap_thumbnails", &settings->bools.menu_rgui_swap_thumbnails, true, rgui_swap_thumbnails, false); @@ -1716,6 +1715,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("rgui_internal_upscale_level", &settings->uints.menu_rgui_internal_upscale_level, true, rgui_internal_upscale_level, false); SETTING_UINT("rgui_aspect_ratio", &settings->uints.menu_rgui_aspect_ratio, true, rgui_aspect, false); SETTING_UINT("rgui_aspect_ratio_lock", &settings->uints.menu_rgui_aspect_ratio_lock, true, rgui_aspect_lock, false); + SETTING_UINT("rgui_particle_effect", &settings->uints.menu_rgui_particle_effect, true, rgui_particle_effect, false); #endif #ifdef HAVE_LIBNX SETTING_UINT("split_joycon_p1", &settings->uints.input_split_joycon[0], true, 0, false); diff --git a/configuration.h b/configuration.h index 199361ffcc..3263ddc622 100644 --- a/configuration.h +++ b/configuration.h @@ -173,7 +173,6 @@ typedef struct settings bool menu_rgui_border_filler_enable; bool menu_rgui_full_width_layout; bool menu_rgui_shadows; - bool menu_rgui_snow; bool menu_rgui_inline_thumbnails; bool menu_rgui_swap_thumbnails; bool menu_rgui_extended_ascii; @@ -455,6 +454,7 @@ typedef struct settings unsigned menu_rgui_internal_upscale_level; unsigned menu_rgui_aspect_ratio; unsigned menu_rgui_aspect_ratio_lock; + unsigned menu_rgui_particle_effect; unsigned menu_ticker_type; unsigned playlist_show_inline_core_name; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 083258a442..3a8408cf7d 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1627,8 +1627,8 @@ MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_FULL_WIDTH_LAYOUT, "menu_rgui_full_width_layout") MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_SHADOWS, "menu_rgui_shadows") -MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_SNOW, - "menu_rgui_snow") +MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_PARTICLE_EFFECT, + "rgui_particle_effect") MSG_HASH(MENU_ENUM_LABEL_MENU_RGUI_EXTENDED_ASCII, "rgui_extended_ascii") MSG_HASH(MENU_ENUM_LABEL_CONTENT_SHOW_REWIND, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 7a911cafeb..ceb0784410 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -6959,13 +6959,37 @@ MSG_HASH( "Enable drop shadows for menu text, borders and thumbnails. Has a modest performance impact." ) MSG_HASH( - MENU_ENUM_LABEL_VALUE_MENU_RGUI_SNOW, - "Snow" + MENU_ENUM_LABEL_VALUE_MENU_RGUI_PARTICLE_EFFECT, + "Background Animation" ) MSG_HASH( - MENU_ENUM_SUBLABEL_MENU_RGUI_SNOW, - "Enable snow, forces menu to update every frame" + MENU_ENUM_SUBLABEL_MENU_RGUI_PARTICLE_EFFECT, + "Enable background particle animation effect. Has a significant performance impact." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_NONE, + "OFF" +) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_SNOW, + "Snow (Light)" +) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_SNOW_ALT, + "Snow (Heavy)" +) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_RAIN, + "Rain" +) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_VORTEX, + "Vortex" +) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_STARFIELD, + "Star Field" +) MSG_HASH( MENU_ENUM_LABEL_VALUE_MENU_RGUI_EXTENDED_ASCII, "Extended ASCII Support" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 4c11c837a2..4ed457b353 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -535,7 +535,7 @@ default_sublabel_macro(action_bind_sublabel_menu_rgui_aspect_ratio_lock, default_sublabel_macro(action_bind_sublabel_rgui_menu_color_theme, MENU_ENUM_SUBLABEL_RGUI_MENU_COLOR_THEME) default_sublabel_macro(action_bind_sublabel_rgui_menu_theme_preset, MENU_ENUM_SUBLABEL_RGUI_MENU_THEME_PRESET) default_sublabel_macro(action_bind_sublabel_menu_rgui_shadows, MENU_ENUM_SUBLABEL_MENU_RGUI_SHADOWS) -default_sublabel_macro(action_bind_sublabel_menu_rgui_snow, MENU_ENUM_SUBLABEL_MENU_RGUI_SNOW) +default_sublabel_macro(action_bind_sublabel_menu_rgui_particle_effect, MENU_ENUM_SUBLABEL_MENU_RGUI_PARTICLE_EFFECT) default_sublabel_macro(action_bind_sublabel_menu_rgui_inline_thumbnails, MENU_ENUM_SUBLABEL_MENU_RGUI_INLINE_THUMBNAILS) default_sublabel_macro(action_bind_sublabel_menu_rgui_swap_thumbnails, MENU_ENUM_SUBLABEL_MENU_RGUI_SWAP_THUMBNAILS) default_sublabel_macro(action_bind_sublabel_menu_rgui_thumbnail_downscaler, MENU_ENUM_SUBLABEL_MENU_RGUI_THUMBNAIL_DOWNSCALER) @@ -2452,8 +2452,8 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_MENU_RGUI_SHADOWS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_shadows); break; - case MENU_ENUM_LABEL_MENU_RGUI_SNOW: - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_snow); + case MENU_ENUM_LABEL_MENU_RGUI_PARTICLE_EFFECT: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_particle_effect); break; case MENU_ENUM_LABEL_MENU_RGUI_INLINE_THUMBNAILS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_rgui_inline_thumbnails); diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index ce637c524e..aaf78840bc 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -80,6 +80,12 @@ #define NUM_FONT_GLYPHS_REGULAR 128 #define NUM_FONT_GLYPHS_EXTENDED 256 +#define NUM_PARTICLES 256 + +#ifndef PI +#define PI 3.14159265359f +#endif + typedef struct { unsigned start_x; @@ -101,6 +107,7 @@ typedef struct uint32_t border_dark_color; uint32_t border_light_color; uint32_t shadow_color; + uint32_t particle_color; } rgui_theme_t; static const rgui_theme_t rgui_theme_classic_red = { @@ -111,7 +118,8 @@ static const rgui_theme_t rgui_theme_classic_red = { 0xC0404040, /* bg_light_color */ 0xC08C0000, /* border_dark_color */ 0xC0CC0E03, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC09E8686 /* particle_color */ }; static const rgui_theme_t rgui_theme_classic_orange = { @@ -122,7 +130,8 @@ static const rgui_theme_t rgui_theme_classic_orange = { 0xC0404040, /* bg_light_color */ 0xC0962800, /* border_dark_color */ 0xC0E46C03, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC09E9286 /* particle_color */ }; static const rgui_theme_t rgui_theme_classic_yellow = { @@ -133,7 +142,8 @@ static const rgui_theme_t rgui_theme_classic_yellow = { 0xC0404040, /* bg_light_color */ 0xC0AC7800, /* border_dark_color */ 0xC0F3C60D, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC0999581 /* particle_color */ }; static const rgui_theme_t rgui_theme_classic_green = { @@ -144,7 +154,8 @@ static const rgui_theme_t rgui_theme_classic_green = { 0xC0404040, /* bg_light_color */ 0xC0204020, /* border_dark_color */ 0xC0408040, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC0879E87 /* particle_color */ }; static const rgui_theme_t rgui_theme_classic_blue = { @@ -155,7 +166,8 @@ static const rgui_theme_t rgui_theme_classic_blue = { 0xC0404040, /* bg_light_color */ 0xC0005BA6, /* border_dark_color */ 0xC02E94E2, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC086949E /* particle_color */ }; static const rgui_theme_t rgui_theme_classic_violet = { @@ -166,7 +178,8 @@ static const rgui_theme_t rgui_theme_classic_violet = { 0xC0404040, /* bg_light_color */ 0xC04C0A60, /* border_dark_color */ 0xC0842DCE, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC08E8299 /* particle_color */ }; static const rgui_theme_t rgui_theme_classic_grey = { @@ -177,7 +190,8 @@ static const rgui_theme_t rgui_theme_classic_grey = { 0xC0404040, /* bg_light_color */ 0xC0505050, /* border_dark_color */ 0xC0798A99, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC078828A /* particle_color */ }; static const rgui_theme_t rgui_theme_legacy_red = { @@ -188,7 +202,8 @@ static const rgui_theme_t rgui_theme_legacy_red = { 0xC0B34B41, /* bg_light_color */ 0xC0BF5E58, /* border_dark_color */ 0xC0F27A6F, /* border_light_color */ - 0xC01F0C0A /* shadow_color */ + 0xC01F0C0A, /* shadow_color */ + 0xC0F75431 /* particle_color */ }; static const rgui_theme_t rgui_theme_dark_purple = { @@ -199,7 +214,8 @@ static const rgui_theme_t rgui_theme_dark_purple = { 0xC0663A66, /* bg_light_color */ 0xC0885783, /* border_dark_color */ 0xC0A675A1, /* border_light_color */ - 0xC0140A14 /* shadow_color */ + 0xC0140A14, /* shadow_color */ + 0xC09786A0 /* particle_color */ }; static const rgui_theme_t rgui_theme_midnight_blue = { @@ -210,7 +226,8 @@ static const rgui_theme_t rgui_theme_midnight_blue = { 0xC03C4D5E, /* bg_light_color */ 0xC046586A, /* border_dark_color */ 0xC06D7F91, /* border_light_color */ - 0xC00A0F14 /* shadow_color */ + 0xC00A0F14, /* shadow_color */ + 0xC084849E /* particle_color */ }; static const rgui_theme_t rgui_theme_golden = { @@ -219,9 +236,10 @@ static const rgui_theme_t rgui_theme_golden = { 0xFFFFCC00, /* title_color */ 0xC0B88D0B, /* bg_dark_color */ 0xC0BF962B, /* bg_light_color */ - 0xC0e1ad21, /* border_dark_color */ + 0xC0E1AD21, /* border_dark_color */ 0xC0FCC717, /* border_light_color */ - 0xC0382B03 /* shadow_color */ + 0xC0382B03, /* shadow_color */ + 0xC0F7D15E /* particle_color */ }; static const rgui_theme_t rgui_theme_electric_blue = { @@ -232,7 +250,8 @@ static const rgui_theme_t rgui_theme_electric_blue = { 0xC0007FFF, /* bg_light_color */ 0xC034A5D8, /* border_dark_color */ 0xC070C9FF, /* border_light_color */ - 0xC012294D /* shadow_color */ + 0xC012294D, /* shadow_color */ + 0xC080C7E6 /* particle_color */ }; static const rgui_theme_t rgui_theme_apple_green = { @@ -243,7 +262,8 @@ static const rgui_theme_t rgui_theme_apple_green = { 0xC0688539, /* bg_light_color */ 0xC0608E3A, /* border_dark_color */ 0xC09AB973, /* border_light_color */ - 0xC01F2E19 /* shadow_color */ + 0xC01F2E19, /* shadow_color */ + 0xC0A3C44E /* particle_color */ }; static const rgui_theme_t rgui_theme_volcanic_red = { @@ -254,7 +274,8 @@ static const rgui_theme_t rgui_theme_volcanic_red = { 0xC0BD0F1E, /* bg_light_color */ 0xC0CE2029, /* border_dark_color */ 0xC0FF0000, /* border_light_color */ - 0xC0330D0D /* shadow_color */ + 0xC0330D0D, /* shadow_color */ + 0xC0E67D45 /* particle_color */ }; static const rgui_theme_t rgui_theme_lagoon = { @@ -265,7 +286,8 @@ static const rgui_theme_t rgui_theme_lagoon = { 0xC0526778, /* bg_light_color */ 0xC058848F, /* border_dark_color */ 0xC060909C, /* border_light_color */ - 0xC01C2329 /* shadow_color */ + 0xC01C2329, /* shadow_color */ + 0xC09FB1C7 /* particle_color */ }; static const rgui_theme_t rgui_theme_brogrammer = { @@ -276,7 +298,8 @@ static const rgui_theme_t rgui_theme_brogrammer = { 0xC0242424, /* bg_light_color */ 0xC0E74C3C, /* border_dark_color */ 0xC0E74C3C, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC0606060 /* particle_color */ }; static const rgui_theme_t rgui_theme_dracula = { @@ -287,7 +310,8 @@ static const rgui_theme_t rgui_theme_dracula = { 0xC02F3240, /* bg_light_color */ 0xC06272A4, /* border_dark_color */ 0xC06272A4, /* border_light_color */ - 0xC00F0F0F /* shadow_color */ + 0xC00F0F0F, /* shadow_color */ + 0xC06272A4 /* particle_color */ }; static const rgui_theme_t rgui_theme_fairyfloss = { @@ -298,7 +322,8 @@ static const rgui_theme_t rgui_theme_fairyfloss = { 0xC0675F87, /* bg_light_color */ 0xC08077A8, /* border_dark_color */ 0xC08077A8, /* border_light_color */ - 0xC0262433 /* shadow_color */ + 0xC0262433, /* shadow_color */ + 0xC0C5A3FF /* particle_color */ }; static const rgui_theme_t rgui_theme_flatui = { @@ -309,7 +334,8 @@ static const rgui_theme_t rgui_theme_flatui = { 0xE0ECF0F1, /* bg_light_color */ 0xE095A5A6, /* border_dark_color */ 0xE095A5A6, /* border_light_color */ - 0xE0C3DBDE /* shadow_color */ + 0xE0C3DBDE, /* shadow_color */ + 0xE0B3DFFF /* particle_color */ }; static const rgui_theme_t rgui_theme_gruvbox_dark = { @@ -320,7 +346,8 @@ static const rgui_theme_t rgui_theme_gruvbox_dark = { 0xC03D3D3D, /* bg_light_color */ 0xC099897A, /* border_dark_color */ 0xC099897A, /* border_light_color */ - 0xC0000000 /* shadow_color */ + 0xC0000000, /* shadow_color */ + 0xC098971A /* particle_color */ }; static const rgui_theme_t rgui_theme_gruvbox_light = { @@ -331,7 +358,8 @@ static const rgui_theme_t rgui_theme_gruvbox_light = { 0xE0FBEBC7, /* bg_light_color */ 0xE0928374, /* border_dark_color */ 0xE0928374, /* border_light_color */ - 0xE0D5C4A1 /* shadow_color */ + 0xE0D5C4A1, /* shadow_color */ + 0xE0D5C4A1 /* particle_color */ }; static const rgui_theme_t rgui_theme_hacking_the_kernel = { @@ -342,7 +370,8 @@ static const rgui_theme_t rgui_theme_hacking_the_kernel = { 0xC0000000, /* bg_light_color */ 0xC0036303, /* border_dark_color */ 0xC0036303, /* border_light_color */ - 0xC0154D2B /* shadow_color */ + 0xC0154D2B, /* shadow_color */ + 0xC0008C00 /* particle_color */ }; static const rgui_theme_t rgui_theme_nord = { @@ -353,7 +382,8 @@ static const rgui_theme_t rgui_theme_nord = { 0xC0363C4F, /* bg_light_color */ 0xC04E596E, /* border_dark_color */ 0xC04E596E, /* border_light_color */ - 0xC0040505 /* shadow_color */ + 0xC0040505, /* shadow_color */ + 0xC05E81AC /* particle_color */ }; static const rgui_theme_t rgui_theme_nova = { @@ -364,7 +394,8 @@ static const rgui_theme_t rgui_theme_nova = { 0xC0485B66, /* bg_light_color */ 0xC0627985, /* border_dark_color */ 0xC0627985, /* border_light_color */ - 0xC01E272C /* shadow_color */ + 0xC01E272C, /* shadow_color */ + 0xC0889BA7 /* particle_color */ }; static const rgui_theme_t rgui_theme_one_dark = { @@ -375,7 +406,8 @@ static const rgui_theme_t rgui_theme_one_dark = { 0xC02D323B, /* bg_light_color */ 0xC0495162, /* border_dark_color */ 0xC0495162, /* border_light_color */ - 0xC007080A /* shadow_color */ + 0xC007080A, /* shadow_color */ + 0xC05F697A /* particle_color */ }; static const rgui_theme_t rgui_theme_palenight = { @@ -386,7 +418,8 @@ static const rgui_theme_t rgui_theme_palenight = { 0xC02F3347, /* bg_light_color */ 0xC0697098, /* border_dark_color */ 0xC0697098, /* border_light_color */ - 0xC00D0E14 /* shadow_color */ + 0xC00D0E14, /* shadow_color */ + 0xC0697098 /* particle_color */ }; static const rgui_theme_t rgui_theme_solarized_dark = { @@ -397,7 +430,8 @@ static const rgui_theme_t rgui_theme_solarized_dark = { 0xC0003542, /* bg_light_color */ 0xC093A1A1, /* border_dark_color */ 0xC093A1A1, /* border_light_color */ - 0xC000141A /* shadow_color */ + 0xC000141A, /* shadow_color */ + 0xC0586E75 /* particle_color */ }; static const rgui_theme_t rgui_theme_solarized_light = { @@ -408,7 +442,8 @@ static const rgui_theme_t rgui_theme_solarized_light = { 0xE0FDEDDF, /* bg_light_color */ 0xE093A1A1, /* border_dark_color */ 0xE093A1A1, /* border_light_color */ - 0xE0E0DBC9 /* shadow_color */ + 0xE0E0DBC9, /* shadow_color */ + 0xE0FFC5AD /* particle_color */ }; static const rgui_theme_t rgui_theme_tango_dark = { @@ -419,7 +454,8 @@ static const rgui_theme_t rgui_theme_tango_dark = { 0xC0384042, /* bg_light_color */ 0xC06A767A, /* border_dark_color */ 0xC06A767A, /* border_light_color */ - 0xC01A1A1A /* shadow_color */ + 0xC01A1A1A, /* shadow_color */ + 0xC0C4A000 /* particle_color */ }; static const rgui_theme_t rgui_theme_tango_light = { @@ -430,7 +466,8 @@ static const rgui_theme_t rgui_theme_tango_light = { 0xE0EEEEEC, /* bg_light_color */ 0xE0C7C7C7, /* border_dark_color */ 0xE0C7C7C7, /* border_light_color */ - 0xE0D3D7CF /* shadow_color */ + 0xE0D3D7CF, /* shadow_color */ + 0xE0FFCA78 /* particle_color */ }; static const rgui_theme_t rgui_theme_zenburn = { @@ -441,7 +478,8 @@ static const rgui_theme_t rgui_theme_zenburn = { 0xC04F4F4F, /* bg_light_color */ 0xC0636363, /* border_dark_color */ 0xC0636363, /* border_light_color */ - 0xC01F1F1F /* shadow_color */ + 0xC01F1F1F, /* shadow_color */ + 0xC0AC7373 /* particle_color */ }; static const rgui_theme_t rgui_theme_anti_zenburn = { @@ -452,7 +490,8 @@ static const rgui_theme_t rgui_theme_anti_zenburn = { 0xE0C0C0C0, /* bg_light_color */ 0xE0A0A0A0, /* border_dark_color */ 0xE0A0A0A0, /* border_light_color */ - 0xE0B0B0B0 /* shadow_color */ + 0xE0B0B0B0, /* shadow_color */ + 0xE0B090B0 /* particle_color */ }; typedef struct @@ -465,6 +504,7 @@ typedef struct uint16_t border_dark_color; uint16_t border_light_color; uint16_t shadow_color; + uint16_t particle_color; } rgui_colors_t; typedef struct @@ -484,7 +524,7 @@ typedef struct bool border_thickness; bool border_enable; bool shadow_enable; - bool snow_enable; + unsigned particle_effect; bool extended_ascii_enable; float scroll_y; char *msgbox; @@ -519,7 +559,24 @@ static unsigned mini_thumbnail_max_height = 0; static bool font_lut[NUM_FONT_GLYPHS_EXTENDED][FONT_WIDTH * FONT_HEIGHT]; -float snowflakes[1024]; +/* A 'particle' is just 4 float variables that can + * be used for any purpose - e.g.: + * > a = x pos + * > b = y pos + * > c = x velocity + * or: + * > a = radius + * > b = theta + * etc. */ +typedef struct +{ + float a; + float b; + float c; + float d; +} rgui_particle_t; + +static rgui_particle_t particles[NUM_PARTICLES] = {{ 0.0f }}; /* ============================== * Custom Symbols (glyphs) START @@ -835,6 +892,420 @@ static void rgui_color_rect( data[j * (pitch >> 1) + i] = color; } +static void rgui_render_border(rgui_t *rgui, uint16_t *data, + size_t fb_pitch, unsigned fb_width, unsigned fb_height) +{ + /* Sanity check */ + if (!rgui || !data) + return; + + /* Draw border */ + rgui_fill_rect(rgui, data, fb_pitch, 5, 5, fb_width - 10, 5, rgui_border_filler); + rgui_fill_rect(rgui, data, fb_pitch, 5, fb_height - 10, fb_width - 10, 5, rgui_border_filler); + rgui_fill_rect(rgui, data, fb_pitch, 5, 5, 5, fb_height - 10, rgui_border_filler); + rgui_fill_rect(rgui, data, fb_pitch, fb_width - 10, 5, 5, fb_height - 10, rgui_border_filler); + + /* Draw drop shadow, if required */ + if (rgui->shadow_enable) + { + rgui_color_rect(data, fb_pitch, fb_width, fb_height, + 10, 10, 1, fb_height - 20, rgui->colors.shadow_color); + rgui_color_rect(data, fb_pitch, fb_width, fb_height, + 10, 10, fb_width - 20, 1, rgui->colors.shadow_color); + rgui_color_rect(data, fb_pitch, fb_width, fb_height, + fb_width - 5, 6, 1, fb_height - 10, rgui->colors.shadow_color); + rgui_color_rect(data, fb_pitch, fb_width, fb_height, + 6, fb_height - 5, fb_width - 10, 1, rgui->colors.shadow_color); + } +} + +/* Returns true if particle is on screen */ +static bool INLINE rgui_draw_particle( + uint16_t *data, + unsigned fb_width, unsigned fb_height, + int x, int y, + unsigned width, unsigned height, + uint16_t color) +{ + unsigned x_index, y_index; + + /* This great convoluted mess just saves us + * having to perform comparisons on every + * iteration of the for loops... */ + int x_start = x > 0 ? x : 0; + int y_start = y > 0 ? y : 0; + int x_end = x + width; + int y_end = y + height; + + x_start = x_start <= fb_width ? x_start : fb_width; + y_start = y_start <= fb_height ? y_start : fb_height; + + x_end = x_end > 0 ? x_end : 0; + x_end = x_end <= fb_width ? x_end : fb_width; + + y_end = y_end > 0 ? y_end : 0; + y_end = y_end <= fb_height ? y_end : fb_height; + + for (x_index = (unsigned)x_start; x_index < (unsigned)x_end; x_index++) + for (y_index = (unsigned)y_start; y_index < (unsigned)y_end; y_index++) + data[(y_index * fb_width) + x_index] = color; + + return (x_end > x_start) && (y_end > y_start); +} + +static void rgui_init_particle_effect(rgui_t *rgui) +{ + size_t fb_pitch; + unsigned fb_width, fb_height; + size_t i; + + /* Sanity check */ + if (!rgui) + return; + + menu_display_get_fb_size(&fb_width, &fb_height, &fb_pitch); + + switch (rgui->particle_effect) + { + case RGUI_PARTICLE_EFFECT_SNOW: + case RGUI_PARTICLE_EFFECT_SNOW_ALT: + { + for (i = 0; i < NUM_PARTICLES; i++) + { + rgui_particle_t *particle = &particles[i]; + + particle->a = (float)(rand() % fb_width); + particle->b = (float)(rand() % fb_height); + particle->c = (float)(rand() % 64 - 16) * 0.1f; + particle->d = (float)(rand() % 64 - 48) * 0.1f; + } + } + break; + case RGUI_PARTICLE_EFFECT_RAIN: + { + uint8_t weights[] = { /* 60 entries */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, + 8, 8, 8, + 9, 9, + 10}; + unsigned num_drops = (unsigned)(0.85f * ((float)fb_width / 426.0f) * (float)NUM_PARTICLES); + + num_drops = num_drops < NUM_PARTICLES ? num_drops : NUM_PARTICLES; + + for (i = 0; i < num_drops; i++) + { + rgui_particle_t *particle = &particles[i]; + + /* x pos */ + particle->a = (float)(rand() % (fb_width / 3)) * 3.0f; + /* y pos */ + particle->b = (float)(rand() % fb_height); + /* drop length */ + particle->c = (float)weights[(unsigned)(rand() % 61)]; + /* drop speed (larger drops fall faster) */ + particle->d = (particle->c / 12.0f) * (0.5f + ((float)(rand() % 150) / 200.0f)); + } + } + break; + case RGUI_PARTICLE_EFFECT_VORTEX: + { + float max_radius = (float)sqrt((double)((fb_width * fb_width) + (fb_height * fb_height))) / 2.0f; + float one_degree_radians = PI / 360.0f; + + for (i = 0; i < NUM_PARTICLES; i++) + { + rgui_particle_t *particle = &particles[i]; + + /* radius */ + particle->a = 1.0f + (((float)rand() / (float)RAND_MAX) * max_radius); + /* theta */ + particle->b = ((float)rand() / (float)RAND_MAX) * 2.0f * PI; + /* radial speed */ + particle->c = (float)((rand() % 100) + 1) * 0.001f; + /* rotational speed */ + particle->d = (((float)((rand() % 50) + 1) / 200.0f) + 0.1f) * one_degree_radians; + } + } + break; + case RGUI_PARTICLE_EFFECT_STARFIELD: + { + float min_depth = (float)fb_width / 12.0f; + + for (i = 0; i < NUM_PARTICLES; i++) + { + rgui_particle_t *particle = &particles[i]; + + /* x pos */ + particle->a = (float)(rand() % fb_width); + /* y pos */ + particle->b = (float)(rand() % fb_height); + /* depth */ + particle->c = (float)fb_width; + /* speed */ + particle->d = 1.0f + ((float)(rand() % 20) * 0.01f); + } + } + break; + default: + /* Do nothing... */ + break; + } +} + +static void rgui_render_particle_effect(rgui_t *rgui) +{ + size_t fb_pitch; + unsigned fb_width, fb_height; + size_t i; + + /* Sanity check */ + if (!rgui || !rgui_frame_buf.data) + return; + + menu_display_get_fb_size(&fb_width, &fb_height, &fb_pitch); + + /* Note: It would be more elegant to have 'update' and 'draw' + * as separate functions, since 'update' is the part that + * varies with particle effect whereas 'draw' is always + * pretty much the same. However, this has the following + * disadvantages: + * - It means we have to loop through all particles twice, + * and given that we're already using a heap of CPU cycles + * to draw these effects any further performance overheads + * are to be avoided + * - It locks us into a particular draw style. e.g. What if + * an effect calls for round particles, instead of square + * ones? This would make a mess of any 'standardised' + * drawing + * So we go with the simple option of having the entire + * update/draw sequence here. This results in some code + * repetition, but it has better performance and allows for + * complete flexibility */ + + switch (rgui->particle_effect) + { + case RGUI_PARTICLE_EFFECT_SNOW: + case RGUI_PARTICLE_EFFECT_SNOW_ALT: + { + unsigned particle_size; + bool on_screen; + + for (i = 0; i < NUM_PARTICLES; i++) + { + rgui_particle_t *particle = &particles[i]; + + /* Update particle 'speed' */ + particle->c = particle->c + (float)(rand() % 16 - 9) * 0.01f; + particle->d = particle->d + (float)(rand() % 16 - 7) * 0.01f; + + particle->c = (particle->c < -0.4f) ? -0.4f : particle->c; + particle->c = (particle->c > 0.1f) ? 0.1f : particle->c; + + particle->d = (particle->d < -0.1f) ? -0.1f : particle->d; + particle->d = (particle->d > 0.4f) ? 0.4f : particle->d; + + /* Update particle location */ + particle->a = fmod(particle->a + particle->c, fb_width); + particle->b = fmod(particle->b + particle->d, fb_height); + + /* Get particle size */ + particle_size = 1; + if (rgui->particle_effect == RGUI_PARTICLE_EFFECT_SNOW_ALT) + { + /* Gives the following distribution: + * 1x1: 32 + * 2x2: 128 + * 3x3: 32 */ + if (!(i & 0x2)) + particle_size = 2; + else if ((i & 0x7) == 0x7) + particle_size = 3; + } + + /* Draw particle */ + on_screen = rgui_draw_particle(rgui_frame_buf.data, fb_width, fb_height, + (int)particle->a, (int)particle->b, + particle_size, particle_size, rgui->colors.particle_color); + + /* Reset particle if it has fallen off screen */ + if (!on_screen) + { + particle->a = (particle->a < 0.0f) ? (particle->a + (float)fb_width) : particle->a; + particle->b = (particle->b < 0.0f) ? (particle->b + (float)fb_height) : particle->b; + } + } + } + break; + case RGUI_PARTICLE_EFFECT_RAIN: + { + uint8_t weights[] = { /* 60 entries */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, + 8, 8, 8, + 9, 9, + 10}; + unsigned num_drops = (unsigned)(0.85f * ((float)fb_width / 426.0f) * (float)NUM_PARTICLES); + bool on_screen; + + num_drops = num_drops < NUM_PARTICLES ? num_drops : NUM_PARTICLES; + + for (i = 0; i < num_drops; i++) + { + rgui_particle_t *particle = &particles[i]; + + /* Draw particle */ + on_screen = rgui_draw_particle(rgui_frame_buf.data, fb_width, fb_height, + (int)particle->a, (int)particle->b, + 2, (unsigned)particle->c, rgui->colors.particle_color); + + /* Update y pos */ + particle->b += particle->d; + + /* Reset particle if it has fallen off the bottom of the screen */ + if (!on_screen) + { + /* x pos */ + particle->a = (float)(rand() % (fb_width / 3)) * 3.0f; + /* y pos */ + particle->b = 0.0f; + /* drop length */ + particle->c = (float)weights[(unsigned)(rand() % 61)]; + /* drop speed (larger drops fall faster) */ + particle->d = (particle->c / 12.0f) * (0.5f + ((float)(rand() % 150) / 200.0f)); + } + } + } + break; + case RGUI_PARTICLE_EFFECT_VORTEX: + { + float max_radius = (float)sqrt((double)((fb_width * fb_width) + (fb_height * fb_height))) / 2.0f; + float one_degree_radians = PI / 360.0f; + int x_centre = (int)(fb_width >> 1); + int y_centre = (int)(fb_height >> 1); + unsigned particle_size; + float r_speed, theta_speed; + int x, y; + + for (i = 0; i < NUM_PARTICLES; i++) + { + rgui_particle_t *particle = &particles[i]; + + /* Get particle location */ + x = (int)(particle->a * cos(particle->b)) + x_centre; + y = (int)(particle->a * sin(particle->b)) + y_centre; + + /* Get particle size */ + particle_size = 1 + (unsigned)(((1.0f - ((max_radius - particle->a) / max_radius)) * 3.5f) + 0.5f); + + /* Draw particle */ + rgui_draw_particle(rgui_frame_buf.data, fb_width, fb_height, + x, y, particle_size, particle_size, rgui->colors.particle_color); + + /* Update particle speed */ + r_speed = particle->c; + theta_speed = particle->d; + if ((particle->a > 0.0f) && (particle->a < (float)fb_height)) + { + float base_scale_factor = ((float)fb_height - particle->a) / (float)fb_height; + r_speed *= 1.0f + (base_scale_factor * 8.0f); + theta_speed *= 1.0f + (base_scale_factor * base_scale_factor * 6.0f); + } + particle->a -= r_speed; + particle->b += theta_speed; + + /* Reset particle if it has reached the centre of the screen */ + if (particle->a < 0.0f) + { + /* radius + * Note: In theory, this should be: + * > particle->a = max_radius; + * ...but it turns out that spawning new particles at random + * locations produces a more visually appealing result... */ + particle->a = 1.0f + (((float)rand() / (float)RAND_MAX) * max_radius); + /* theta */ + particle->b = ((float)rand() / (float)RAND_MAX) * 2.0f * PI; + /* radial speed */ + particle->c = (float)((rand() % 100) + 1) * 0.001f; + /* rotational speed */ + particle->d = (((float)((rand() % 50) + 1) / 200.0f) + 0.1f) * one_degree_radians; + } + } + } + break; + case RGUI_PARTICLE_EFFECT_STARFIELD: + { + float focal_length = (float)fb_width * 2.0f; + int x_centre = (int)(fb_width >> 1); + int y_centre = (int)(fb_height >> 1); + unsigned particle_size; + int x, y; + bool on_screen; + + /* Based on an example found here: + * https://codepen.io/nodws/pen/pejBNb */ + for (i = 0; i < NUM_PARTICLES; i++) + { + rgui_particle_t *particle = &particles[i]; + + /* Get particle location */ + x = (int)((particle->a - (float)x_centre) * (focal_length / particle->c)); + x += x_centre; + + y = (int)((particle->b - (float)y_centre) * (focal_length / particle->c)); + y += y_centre; + + /* Get particle size */ + particle_size = (unsigned)(focal_length / (2.0f * particle->c)); + + /* Draw particle */ + on_screen = rgui_draw_particle(rgui_frame_buf.data, fb_width, fb_height, + x, y, particle_size, particle_size, rgui->colors.particle_color); + + /* Update depth */ + particle->c -= particle->d; + + /* Reset particle if it has: + * - Dropped off the edge of the screen + * - Reached the screen depth + * - Grown larger than 16 pixels across + * (this is an arbitrary limit, set to reduce overall + * performance impact - i.e. larger particles are slower + * to draw, and without setting a limit they can fill the screen...) */ + if (!on_screen || (particle->c <= 0.0f) || particle_size > 16) + { + /* x pos */ + particle->a = (float)(rand() % fb_width); + /* y pos */ + particle->b = (float)(rand() % fb_height); + /* depth */ + particle->c = (float)fb_width; + /* speed */ + particle->d = 1.0f + ((float)(rand() % 20) * 0.01f); + } + } + } + break; + default: + /* Do nothing... */ + break; + } + + /* If border is enabled, it must be drawn *above* + * particle effect + * (Wastes CPU cycles, but nothing we can do about it...) */ + if (rgui->border_enable && !rgui->show_wallpaper) + rgui_render_border(rgui, rgui_frame_buf.data, fb_pitch, fb_width, fb_height); +} + static void request_wallpaper(const char *path) { /* Ensure that path is valid... */ @@ -1385,7 +1856,7 @@ static void load_custom_theme(rgui_t *rgui, rgui_theme_t *theme_colors, const ch unsigned normal_color, hover_color, title_color, bg_dark_color, bg_light_color, border_dark_color, border_light_color, - shadow_color; + shadow_color, particle_color; char wallpaper_file[PATH_MAX_LENGTH]; config_file_t *conf = NULL; char *wallpaper_key = NULL; @@ -1450,6 +1921,12 @@ static void load_custom_theme(rgui_t *rgui, rgui_theme_t *theme_colors, const ch if(!config_get_hex(conf, "rgui_shadow_color", &shadow_color)) shadow_color = 0xFF000000; + /* Make particle colour optional too (fallback to normal + * rgb with bg_light alpha) */ + if(!config_get_hex(conf, "rgui_particle_color", &particle_color)) + particle_color = (normal_color & 0x00FFFFFF) | + (bg_light_color & 0xFF000000); + config_get_array(conf, wallpaper_key, wallpaper_file, sizeof(wallpaper_file)); @@ -1467,6 +1944,7 @@ end: theme_colors->border_dark_color = (uint32_t)border_dark_color; theme_colors->border_light_color = (uint32_t)border_light_color; theme_colors->shadow_color = (uint32_t)shadow_color; + theme_colors->particle_color = (uint32_t)particle_color; /* Load wallpaper, if required */ if (!string_is_empty(wallpaper_file)) @@ -1489,6 +1967,7 @@ end: theme_colors->border_dark_color = rgui_theme_classic_green.border_dark_color; theme_colors->border_light_color = rgui_theme_classic_green.border_light_color; theme_colors->shadow_color = rgui_theme_classic_green.shadow_color; + theme_colors->particle_color = rgui_theme_classic_green.particle_color; } if (conf) @@ -1536,35 +2015,7 @@ static void rgui_cache_background(rgui_t *rgui) } if (rgui->border_enable) - { - /* Draw border */ - rgui_fill_rect(rgui, rgui_background_buf.data, fb_pitch, 5, 5, fb_width - 10, 5, rgui_border_filler); - rgui_fill_rect(rgui, rgui_background_buf.data, fb_pitch, 5, fb_height - 10, fb_width - 10, 5, rgui_border_filler); - rgui_fill_rect(rgui, rgui_background_buf.data, fb_pitch, 5, 5, 5, fb_height - 10, rgui_border_filler); - rgui_fill_rect(rgui, rgui_background_buf.data, fb_pitch, fb_width - 10, 5, 5, fb_height - 10, rgui_border_filler); - - /* Draw drop shadow, if required */ - if (rgui->shadow_enable) - { - rgui_color_rect(rgui_background_buf.data, fb_pitch, fb_width, fb_height, - 10, 10, 1, fb_height - 20, rgui->colors.shadow_color); - rgui_color_rect(rgui_background_buf.data, fb_pitch, fb_width, fb_height, - 10, 10, fb_width - 20, 1, rgui->colors.shadow_color); - rgui_color_rect(rgui_background_buf.data, fb_pitch, fb_width, fb_height, - fb_width - 5, 6, 1, fb_height - 10, rgui->colors.shadow_color); - rgui_color_rect(rgui_background_buf.data, fb_pitch, fb_width, fb_height, - 6, fb_height - 5, fb_width - 10, 1, rgui->colors.shadow_color); - } - } - if (rgui->snow_enable) { - size_t i = 0; - for (i = 0; i < 1024; i += 4) { - snowflakes[i ] = rand()%fb_width; - snowflakes[i + 1] = rand()%fb_height; - snowflakes[i + 2] = (rand()%64 - 16)*.1; - snowflakes[i + 3] = (rand()%64 - 48)*.1; - } - } + rgui_render_border(rgui, rgui_background_buf.data, fb_pitch, fb_width, fb_height); } static void prepare_rgui_colors(rgui_t *rgui, settings_t *settings) @@ -1590,6 +2041,7 @@ static void prepare_rgui_colors(rgui_t *rgui, settings_t *settings) theme_colors.border_dark_color = current_theme->border_dark_color; theme_colors.border_light_color = current_theme->border_light_color; theme_colors.shadow_color = current_theme->shadow_color; + theme_colors.particle_color = current_theme->particle_color; } rgui->colors.hover_color = argb32_to_pixel_platform_format(theme_colors.hover_color); rgui->colors.normal_color = argb32_to_pixel_platform_format(theme_colors.normal_color); @@ -1599,6 +2051,7 @@ static void prepare_rgui_colors(rgui_t *rgui, settings_t *settings) rgui->colors.border_dark_color = argb32_to_pixel_platform_format(theme_colors.border_dark_color); rgui->colors.border_light_color = argb32_to_pixel_platform_format(theme_colors.border_light_color); rgui->colors.shadow_color = argb32_to_pixel_platform_format(theme_colors.shadow_color); + rgui->colors.particle_color = argb32_to_pixel_platform_format(theme_colors.particle_color); rgui->bg_modified = true; rgui->force_redraw = true; @@ -2006,40 +2459,28 @@ static void rgui_render_messagebox(rgui_t *rgui, const char *message) { rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x + 5, y + 5, width - 10, height - 10, rgui_bg_filler); - if (rgui->border_enable) - { - /* Draw drop shadow, if required */ - if (rgui->shadow_enable) - { - rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + 5, y + 5, 1, height - 5, rgui->colors.shadow_color); - rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + 5, y + 5, width - 5, 1, rgui->colors.shadow_color); - rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + width, y + 1, 1, height, rgui->colors.shadow_color); - rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + 1, y + height, width, 1, rgui->colors.shadow_color); - } + /* Note: We draw borders around message boxes regardless + * of the rgui->border_enable setting, because they look + * ridiculous without... */ - /* Draw border */ - rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x, y, width - 5, 5, rgui_border_filler); - rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x + width - 5, y, 5, height - 5, rgui_border_filler); - rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x + 5, y + height - 5, width - 5, 5, rgui_border_filler); - rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x, y + 5, 5, height - 5, rgui_border_filler); - } - else if (rgui->shadow_enable) + /* Draw drop shadow, if required */ + if (rgui->shadow_enable) { - /* Without a border, this is a bit silly... - * All we can do is draw a sort of frame... */ rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + 4, y + 4, 1, height - 8, rgui->colors.shadow_color); + x + 5, y + 5, 1, height - 5, rgui->colors.shadow_color); rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + 5, y + 4, width - 10, 1, rgui->colors.shadow_color); + x + 5, y + 5, width - 5, 1, rgui->colors.shadow_color); rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + width - 5, y + 4, 1, height - 8, rgui->colors.shadow_color); + x + width, y + 1, 1, height, rgui->colors.shadow_color); rgui_color_rect(rgui_frame_buf.data, fb_pitch, fb_width, fb_height, - x + 5, y + height - 5, width - 10, 1, rgui->colors.shadow_color); + x + 1, y + height, width, 1, rgui->colors.shadow_color); } + + /* Draw border */ + rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x, y, width - 5, 5, rgui_border_filler); + rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x + width - 5, y, 5, height - 5, rgui_border_filler); + rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x + 5, y + height - 5, width - 5, 5, rgui_border_filler); + rgui_fill_rect(rgui, rgui_frame_buf.data, fb_pitch, x, y + 5, 5, height - 5, rgui_border_filler); } for (i = 0; i < list->size; i++) @@ -2328,6 +2769,7 @@ static void rgui_render(void *data, bool is_idle) int bottom; size_t entries_end = 0; bool msg_force = false; + bool fb_size_changed = false; settings_t *settings = config_get_ptr(); rgui_t *rgui = (rgui_t*)data; @@ -2364,10 +2806,17 @@ static void rgui_render(void *data, bool is_idle) menu_display_get_fb_size(&fb_width, &fb_height, &fb_pitch); - /* if the framebuffer changed size, recache the background */ - if (rgui->bg_modified || rgui->last_width != fb_width || rgui->last_height != fb_height) + /* If the framebuffer changed size, or the background config has + * changed, recache the background buffer */ + fb_size_changed = (rgui->last_width != fb_width) || (rgui->last_height != fb_height); + if (rgui->bg_modified || fb_size_changed) { rgui_cache_background(rgui); + + /* Reinitialise particle effect, if required */ + if (fb_size_changed && (rgui->particle_effect != RGUI_PARTICLE_EFFECT_NONE)) + rgui_init_particle_effect(rgui); + rgui->last_width = fb_width; rgui->last_height = fb_height; } @@ -2440,23 +2889,10 @@ static void rgui_render(void *data, bool is_idle) /* Render background */ rgui_render_background(); - - /* Snow */ - if (rgui->snow_enable && rgui_frame_buf.data) { - for (i = 0; i < 1024; i += 4) { - snowflakes[i + 2] = snowflakes[i + 2] + (rand()%16 - 9)*.01; - snowflakes[i + 3] = snowflakes[i + 3] + (rand()%16 - 7)*.01; - if (snowflakes[i + 2] < -0.4) snowflakes[i + 2] = -0.4; - if (snowflakes[i + 2] > 0.1) snowflakes[i + 2] = 0.1; - if (snowflakes[i + 3] < -0.1) snowflakes[i + 3] = -0.1; - if (snowflakes[i + 3] > 0.4) snowflakes[i + 3] = 0.4; - snowflakes[i ] = fmod(snowflakes[i ] + snowflakes[i + 2], fb_width); - snowflakes[i + 1] = fmod(snowflakes[i + 1] + snowflakes[i + 3], fb_height); - if (snowflakes[i ] < 0) snowflakes[i ] += fb_width; - if (snowflakes[i + 1] < 0) snowflakes[i + 1] += fb_height; - rgui_frame_buf.data[ (int) snowflakes[i+1] * (fb_pitch >> 1) + (int) snowflakes[i]] = rgui->colors.normal_color; - } - } + + /* Render particle effect, if required */ + if (rgui->particle_effect != RGUI_PARTICLE_EFFECT_NONE) + rgui_render_particle_effect(rgui); /* We use a single ticker for all text animations, * with the following configuration: */ @@ -3172,12 +3608,16 @@ static void *rgui_init(void **userdata, bool video_is_threaded) rgui->border_thickness = settings->bools.menu_rgui_border_filler_thickness_enable; rgui->border_enable = settings->bools.menu_rgui_border_filler_enable; rgui->shadow_enable = settings->bools.menu_rgui_shadows; - rgui->snow_enable = settings->bools.menu_rgui_snow; + rgui->particle_effect = settings->uints.menu_rgui_particle_effect; rgui->extended_ascii_enable = settings->bools.menu_rgui_extended_ascii; rgui->last_width = rgui_frame_buf.width; rgui->last_height = rgui_frame_buf.height; + /* Initialise particle effect, if required */ + if (rgui->particle_effect != RGUI_PARTICLE_EFFECT_NONE) + rgui_init_particle_effect(rgui); + /* Set initial 'blit_line/symbol' functions */ rgui_set_blit_functions( settings->bools.menu_rgui_shadows, settings->bools.menu_rgui_extended_ascii); @@ -3667,8 +4107,6 @@ static void rgui_frame(void *data, video_frame_info_t *video_info) { rgui_t *rgui = (rgui_t*)data; settings_t *settings = config_get_ptr(); - if (rgui->snow_enable) - rgui->force_redraw = true; if (settings->bools.menu_rgui_background_filler_thickness_enable != rgui->bg_thickness) { @@ -3701,12 +4139,19 @@ static void rgui_frame(void *data, video_frame_info_t *video_info) rgui->force_redraw = true; } - if (settings->bools.menu_rgui_snow != rgui->snow_enable) + if (settings->uints.menu_rgui_particle_effect != rgui->particle_effect) { - rgui->snow_enable = settings->bools.menu_rgui_snow; - rgui->bg_modified = true; + rgui->particle_effect = settings->uints.menu_rgui_particle_effect; + + if (rgui->particle_effect != RGUI_PARTICLE_EFFECT_NONE) + rgui_init_particle_effect(rgui); + + rgui->force_redraw = true; } + if (rgui->particle_effect != RGUI_PARTICLE_EFFECT_NONE) + rgui->force_redraw = true; + if (settings->bools.menu_rgui_extended_ascii != rgui->extended_ascii_enable) { rgui_set_blit_functions( diff --git a/menu/menu_defines.h b/menu/menu_defines.h index 8ea24d035d..9fe47bbfa4 100644 --- a/menu/menu_defines.h +++ b/menu/menu_defines.h @@ -267,6 +267,17 @@ enum rgui_aspect_ratio_lock RGUI_ASPECT_RATIO_LOCK_LAST }; +enum rgui_particle_animation_effect +{ + RGUI_PARTICLE_EFFECT_NONE = 0, + RGUI_PARTICLE_EFFECT_SNOW, + RGUI_PARTICLE_EFFECT_SNOW_ALT, + RGUI_PARTICLE_EFFECT_RAIN, + RGUI_PARTICLE_EFFECT_VORTEX, + RGUI_PARTICLE_EFFECT_STARFIELD, + RGUI_PARTICLE_EFFECT_LAST +}; + enum menu_action { MENU_ACTION_NOOP = 0, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 8420fbc327..b4d010d6b3 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4306,7 +4306,7 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct {MENU_ENUM_LABEL_RGUI_MENU_COLOR_THEME, PARSE_ONLY_UINT}, {MENU_ENUM_LABEL_RGUI_MENU_THEME_PRESET, PARSE_ONLY_PATH}, {MENU_ENUM_LABEL_MENU_RGUI_SHADOWS, PARSE_ONLY_BOOL}, - {MENU_ENUM_LABEL_MENU_RGUI_SNOW, PARSE_ONLY_BOOL}, + {MENU_ENUM_LABEL_MENU_RGUI_PARTICLE_EFFECT, PARSE_ONLY_UINT}, {MENU_ENUM_LABEL_DPI_OVERRIDE_ENABLE, PARSE_ONLY_BOOL}, {MENU_ENUM_LABEL_DPI_OVERRIDE_VALUE, PARSE_ONLY_UINT}, {MENU_ENUM_LABEL_XMB_ALPHA_FACTOR, PARSE_ONLY_UINT}, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 0be5eb29f9..2259fe6bff 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -970,6 +970,54 @@ static void setting_get_string_representation_uint_rgui_aspect_ratio_lock( } } +static void setting_get_string_representation_uint_rgui_particle_effect( + rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + switch (*setting->value.target.unsigned_integer) + { + case RGUI_PARTICLE_EFFECT_NONE: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_NONE), + len); + break; + case RGUI_PARTICLE_EFFECT_SNOW: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_SNOW), + len); + break; + case RGUI_PARTICLE_EFFECT_SNOW_ALT: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_SNOW_ALT), + len); + break; + case RGUI_PARTICLE_EFFECT_RAIN: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_RAIN), + len); + break; + case RGUI_PARTICLE_EFFECT_VORTEX: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_VORTEX), + len); + break; + case RGUI_PARTICLE_EFFECT_STARFIELD: + strlcpy(s, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_STARFIELD), + len); + break; + } +} + static void setting_get_string_representation_uint_menu_ticker_type( rarch_setting_t *setting, char *s, size_t len) @@ -8599,20 +8647,22 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); - CONFIG_BOOL( + CONFIG_UINT( list, list_info, - &settings->bools.menu_rgui_snow, - MENU_ENUM_LABEL_MENU_RGUI_SNOW, - MENU_ENUM_LABEL_VALUE_MENU_RGUI_SNOW, - rgui_snow, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, + &settings->uints.menu_rgui_particle_effect, + MENU_ENUM_LABEL_MENU_RGUI_PARTICLE_EFFECT, + MENU_ENUM_LABEL_VALUE_MENU_RGUI_PARTICLE_EFFECT, + rgui_particle_effect, &group_info, &subgroup_info, parent_group, general_write_handler, - general_read_handler, - SD_FLAG_NONE); + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_uint_rgui_particle_effect; + menu_settings_list_current_add_range(list, list_info, 0, RGUI_PARTICLE_EFFECT_LAST-1, 1, true, true); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX; CONFIG_BOOL( list, list_info, diff --git a/msg_hash.h b/msg_hash.h index c6302d7190..095a4263c3 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -873,7 +873,7 @@ enum msg_hash_enums MENU_LABEL(MENU_RGUI_ASPECT_RATIO_LOCK), MENU_LABEL(MENU_RGUI_FULL_WIDTH_LAYOUT), MENU_LABEL(MENU_RGUI_SHADOWS), - MENU_LABEL(MENU_RGUI_SNOW), + MENU_LABEL(MENU_RGUI_PARTICLE_EFFECT), MENU_LABEL(MENU_RGUI_EXTENDED_ASCII), MENU_LABEL(MENU_LINEAR_FILTER), MENU_LABEL(MENU_HORIZONTAL_ANIMATION), @@ -2016,6 +2016,13 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_RGUI_ASPECT_RATIO_LOCK_FIT_SCREEN, MENU_ENUM_LABEL_VALUE_RGUI_ASPECT_RATIO_LOCK_INTEGER, + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_NONE, + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_SNOW, + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_SNOW_ALT, + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_RAIN, + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_VORTEX, + MENU_ENUM_LABEL_VALUE_RGUI_PARTICLE_EFFECT_STARFIELD, + /* Callback strings */ MENU_ENUM_LABEL_CB_CORE_CONTENT_DIRS_LIST, MENU_ENUM_LABEL_CB_CORE_CONTENT_DOWNLOAD,