diff --git a/Core/Config.h b/Core/Config.h index 9589ffe6e6c8..a7fcd1c147ee 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -38,11 +38,6 @@ enum ChatPositions { CENTER_RIGHT = 7, }; -enum class BackgroundAnimation { - OFF = 0, - FLOATING_SYMBOLS = 1, -}; - namespace http { class Download; class Downloader; diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 8936f638097e..d55af779647d 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -116,3 +116,9 @@ enum class UnthrottleMode { SKIP_DRAW = 1, SKIP_FLIP = 2, }; + +enum class BackgroundAnimation { + OFF = 0, + FLOATING_SYMBOLS = 1, + RECENT_GAMES = 2, +}; diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 8563d111f9eb..ce9a2bf7b702 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -100,6 +100,14 @@ class GameInfo { std::string GetTitle(); void SetTitle(const std::string &newTitle); + GameInfoTex *GetBGPic() { + if (pic0.texture) + return &pic0; + if (pic1.texture) + return &pic1; + return nullptr; + } + // Hold this when reading or writing from the GameInfo. // Don't need to hold it when just passing around the pointer, // and obviously also not when creating it and holding the only pointer diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7ad03db35dfb..ba7724412480 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -875,7 +875,7 @@ void GameSettingsScreen::CreateViews() { if (backgroundChoice_ != nullptr) { backgroundChoice_->OnClick.Handle(this, &GameSettingsScreen::OnChangeBackground); } - static const char *backgroundAnimations[] = { "No animation", "Floating Symbols" }; + static const char *backgroundAnimations[] = { "No animation", "Floating symbols", "Recent games" }; systemSettings->Add(new PopupMultiChoice(&g_Config.iBackgroundAnimation, sy->T("UI background animation"), backgroundAnimations, 0, ARRAY_SIZE(backgroundAnimations), sy->GetName(), screenManager())); systemSettings->Add(new ItemHeader(sy->T("Help the PPSSPP team"))); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 8ab0288b8be7..90be90c5010b 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1309,12 +1309,8 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, f return false; } - Draw::Texture *texture = nullptr; - if (ginfo->pic1.texture) { - texture = ginfo->pic1.texture->GetTexture(); - } else if (ginfo->pic0.texture) { - texture = ginfo->pic0.texture->GetTexture(); - } + auto pic = ginfo->GetBGPic(); + Draw::Texture *texture = pic ? pic->texture->GetTexture() : nullptr; uint32_t color = whiteAlpha(ease(progress)) & 0xFFc0c0c0; if (texture) { diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 1e8271b9dd0d..c5e8a9f7201b 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -85,17 +85,11 @@ class FloatingSymbolsAnimation : public Animation { void Draw(UIContext &dc, double t, float alpha) override { float xres = dc.GetBounds().w; float yres = dc.GetBounds().h; - if (xbase[0] == 0.0f || last_xres != xres || last_yres != yres) { - GMRng rng; - for (int i = 0; i < 100; i++) { - xbase[i] = rng.F() * xres; - ybase[i] = rng.F() * yres; - } - last_xres = xres; - last_yres = yres; + if (last_xres != xres || last_yres != yres) { + Regenerate(xres, yres); } - for (int i = 0; i < 100; i++) { + for (int i = 0; i < COUNT; i++) { float x = xbase[i] + dc.GetBounds().x; float y = ybase[i] + dc.GetBounds().y + 40 * cosf(i * 7.2f + t * 1.3f); float angle = (float)sin(i + t); @@ -103,11 +97,106 @@ class FloatingSymbolsAnimation : public Animation { ui_draw2d.DrawImageRotated(symbols[n], x, y, 1.0f, angle, colorAlpha(colors[n], alpha * 0.1f)); } } + private: - float xbase[100] = { 0 }; - float ybase[100] = { 0 }; + static constexpr int COUNT = 100; + + float xbase[COUNT]{}; + float ybase[COUNT]{}; float last_xres = 0; float last_yres = 0; + + void Regenerate(int xres, int yres) { + GMRng rng; + for (int i = 0; i < COUNT; i++) { + xbase[i] = rng.F() * xres; + ybase[i] = rng.F() * yres; + } + + last_xres = xres; + last_yres = yres; + } +}; + +class RecentGamesAnimation : public Animation { +public: + ~RecentGamesAnimation() override {} + void Draw(UIContext &dc, double t, float alpha) override { + if (lastIndex_ == nextIndex_) { + CheckNext(dc, t); + } else if (t > nextT_) { + lastIndex_ = nextIndex_; + } + + if (!g_Config.recentIsos.empty()) { + std::shared_ptr lastInfo = GetInfo(dc, lastIndex_); + std::shared_ptr nextInfo = GetInfo(dc, nextIndex_); + dc.Flush(); + + float lastAmount = Clamp((float)(nextT_ - t) * 1.0f / TRANSITION, 0.0f, 1.0f); + DrawTex(dc, lastInfo, lastAmount * alpha * 0.2f); + + float nextAmount = lastAmount <= 0.0f ? 1.0f : 1.0f - lastAmount; + DrawTex(dc, nextInfo, nextAmount * alpha * 0.2f); + + dc.RebindTexture(); + } + } + +private: + void CheckNext(UIContext &dc, double t) { + if (g_Config.recentIsos.empty()) { + return; + } + + for (int index = lastIndex_ + 1; index != lastIndex_; ++index) { + if (index < 0 || index >= (int)g_Config.recentIsos.size()) { + if (lastIndex_ == -1) + break; + index = 0; + } + + std::shared_ptr ginfo = GetInfo(dc, index); + if (ginfo && ginfo->pending) { + // Wait for it to load. It might be the next one. + break; + } + if (ginfo && (ginfo->pic1.texture || ginfo->pic0.texture)) { + nextIndex_ = index; + nextT_ = t + INTERVAL; + break; + } + + // Otherwise, keep going. This skips games with no BG. + } + } + + std::shared_ptr GetInfo(UIContext &dc, int index) { + if (index < 0) { + return nullptr; + } + return g_gameInfoCache->GetInfo(dc.GetDrawContext(), g_Config.recentIsos[index], GAMEINFO_WANTBG); + } + + void DrawTex(UIContext &dc, std::shared_ptr &ginfo, float amount) { + if (!ginfo || amount <= 0.0f) + return; + GameInfoTex *pic = ginfo->GetBGPic(); + if (!pic) + return; + + dc.GetDrawContext()->BindTexture(0, pic->texture->GetTexture()); + uint32_t color = whiteAlpha(amount) & 0xFFc0c0c0; + dc.Draw()->DrawTexRect(dc.GetBounds(), 0, 0, 1, 1, color); + dc.Flush(); + } + + static constexpr double INTERVAL = 8.0; + static constexpr float TRANSITION = 3.0f; + + int lastIndex_ = -1; + int nextIndex_ = -1; + double nextT_ = -INTERVAL; }; // TODO: Add more styles. Remember to add to the enum in Config.cpp and the selector in GameSettings too. @@ -137,6 +226,9 @@ void DrawBackground(UIContext &dc, float alpha) { case BackgroundAnimation::FLOATING_SYMBOLS: g_Animation.reset(new FloatingSymbolsAnimation()); break; + case BackgroundAnimation::RECENT_GAMES: + g_Animation.reset(new RecentGamesAnimation()); + break; default: g_Animation.reset(nullptr); } @@ -177,19 +269,12 @@ void DrawGameBackground(UIContext &dc, const std::string &gamePath) { ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG); dc.Flush(); - bool hasPic = false; - double loadTime; - if (ginfo && ginfo->pic1.texture) { - dc.GetDrawContext()->BindTexture(0, ginfo->pic1.texture->GetTexture()); - loadTime = ginfo->pic1.timeLoaded; - hasPic = true; - } else if (ginfo && ginfo->pic0.texture) { - dc.GetDrawContext()->BindTexture(0, ginfo->pic0.texture->GetTexture()); - loadTime = ginfo->pic0.timeLoaded; - hasPic = true; + GameInfoTex *pic = ginfo ? ginfo->GetBGPic() : nullptr; + if (pic) { + dc.GetDrawContext()->BindTexture(0, pic->texture->GetTexture()); } - if (hasPic) { - uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3)) & 0xFFc0c0c0; + if (pic) { + uint32_t color = whiteAlpha(ease((time_now_d() - pic->timeLoaded) * 3)) & 0xFFc0c0c0; dc.Draw()->DrawTexRect(dc.GetBounds(), 0,0,1,1, color); dc.Flush(); dc.RebindTexture(); diff --git a/UI/TextureUtil.cpp b/UI/TextureUtil.cpp index 3d986e765a80..9a5b377a90d7 100644 --- a/UI/TextureUtil.cpp +++ b/UI/TextureUtil.cpp @@ -236,11 +236,9 @@ void GameIconView::Draw(UIContext &dc) { // Fade icon with the backgrounds. double loadTime = info->icon.timeLoaded; - if (info->pic1.texture) { - loadTime = std::max(loadTime, info->pic1.timeLoaded); - } - if (info->pic0.texture) { - loadTime = std::max(loadTime, info->pic0.timeLoaded); + auto pic = info->GetBGPic(); + if (pic) { + loadTime = std::max(loadTime, pic->timeLoaded); } uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3));