Added multiple animations and effects (mostly using shaders).

This commit is contained in:
Leon Styhre 2020-09-13 13:21:38 +02:00
parent d1adb313e6
commit 66829b4ffa
14 changed files with 257 additions and 48 deletions

View file

@ -8,7 +8,7 @@ First release, a major update to the application compared to the RetroPie versio
Full navigation sound support has been implemented, and the metadata editor has seen a lot of updates including color coding of all changes done by the user and by the scraper. Favorite games can now also be sorted on top of the gamelists and game collections.
OpenGL GLSL shader support has been added (not for the OpenGL ES renderer though) which will open up many possibilities in the future.
OpenGL GLSL shader support has been added (not for the OpenGL ES renderer though) and there are multiple effects implemented such as scanlines for videos, blurred background when opening menus etc.
A new default theme rbsimple-DE (based on Recalbox Multi) is bundled with the application and is part of the installation package/installer. However themes created for other EmulationStation ports should still work correctly.
@ -31,6 +31,7 @@ Many bugs have been fixed, and numerous features that were only partially implem
* Updated the application to compile and work on Microsoft Windows, including full UTF-16 (Unicode) support
* Updated the application to compile and work on Apple macOS
* Added support for OpenGL GLSL shaders (OpenGL 2.1 renderer only, no support for OpenGL ES 1.0 renderer)
* Added multiple animations and shader effects, such as when opening menus, playing videos in the gamelists and via the screensaver etc.
* Seamless (almost) launch of games without showing the desktop when starting and when returning from RetroArch and other emulators
* Per-game launch command override, so that different cores or emulators can be used on a per-game basis (saved to gamelist.xml)
* Core location can be defined relative to the emulator binary using the %EMUPATH% variable in es_systems.cfg (mostly useful for Windows)

View file

@ -547,6 +547,14 @@ Defaults to Full which enables all functionality within the application. If set
The order in which to sort your gamelists. This can be overriden per game system using the game options menu, but that override will only be persistent during the application session.
**Open menu effect** _(OpenGL renderer only)_
Animation to play when opening the main menu or the game options menu. Can be set to _scale-up_, _fade-in_ or _none_.
**Render scanlines for gamelist videos** _(OpenGL renderer only)_
Whether to use a shader to render scanlines for videos in the gamelist view.
**Sort folders on top of gamelists**
Whether to place all folders on top of the gamelists. If done so, the folders will not be part of the quick selector index, meaning they can no longer be quick-jumped to. Also, if this option is enabled, folders marked as favorites will not be sorted above non-favorite folders.
@ -591,22 +599,6 @@ This includes the ability to start the screensaver manually, but also to browse
The screensaven style to use, which includes _Dim_, _Black_, _Slideshow_ and _Video_.
#### Video screensaver settings
Options specific to the video screensaver.
**Swap videos after (secs)**
How long to play videos before change to the next game.
**Stretch videos to screen resolution**
This will fill the entire screen surface but will possibly break the aspect ratio of the video.
**Play audio for screensaver video files**
Muting or playing the audio.
#### Slideshow screensaver settings
Options specific to the slideshow screensaver.
@ -619,6 +611,10 @@ How long to show images before change to the next game.
This will fill the entire screen surface but will possibly break the aspect ratio of the image.
**Render scanlines** _(OpenGL renderer only)_
Whether to use a shader to render scanlines on top of the images.
**Background audio**
Background audio to play when the screensaver is active.
@ -639,6 +635,30 @@ Whether to search the custom image directory recursively.
The file extensions to consider for the custom images.
#### Video screensaver settings
Options specific to the video screensaver.
**Swap videos after (secs)**
How long to play videos before change to the next game.
**Stretch videos to screen resolution**
This will fill the entire screen surface but will possibly break the aspect ratio of the video.
**Play audio for screensaver video files**
Muting or playing the audio.
**Render scanlines** _(OpenGL renderer only)_
Whether to use a shader to render scanlines for the videos.
**Render blur** _(OpenGL renderer only)_
Whether to use a shader to render a slight blur which somewhat simulates a well-used CRT monitor.
### Sound settings

View file

@ -251,10 +251,12 @@ void SystemScreenSaver::renderScreenSaver()
}
}
else if (mState != STATE_INACTIVE) {
#if !defined(USE_OPENGL_21)
Renderer::setMatrix(Transform4x4f::Identity());
unsigned char color = screensaver_behavior == "dim" ? 0x000000A0 : 0x000000FF;
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), color, color);
#endif
}
}

View file

@ -32,7 +32,8 @@ public:
virtual FileData* getCurrentGame();
virtual void launchGame();
inline virtual void resetCounts() { mVideosCounted = false; mImagesCounted = false; };
virtual void resetCounts() { mVideosCounted = false; mImagesCounted = false; };
virtual unsigned int getVideoCount() { return mVideoCount; };
private:
unsigned long countGameListNodes(const char *nodeName);

View file

@ -66,14 +66,6 @@ GuiGeneralScreensaverOptions::GuiGeneralScreensaverOptions(Window* window, const
ComponentListRow row;
// Show filtered menu.
row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow,
"VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(
&GuiGeneralScreensaverOptions::openVideoScreensaverOptions, this));
addRow(row);
row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow,
"SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
@ -81,6 +73,14 @@ GuiGeneralScreensaverOptions::GuiGeneralScreensaverOptions(Window* window, const
row.makeAcceptInputHandler(std::bind(
&GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions, this));
addRow(row);
row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow,
"VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(
&GuiGeneralScreensaverOptions::openVideoScreensaverOptions, this));
addRow(row);
}
GuiGeneralScreensaverOptions::~GuiGeneralScreensaverOptions()

View file

@ -387,6 +387,38 @@ void GuiMenu::openUISettings()
}
});
#if defined(USE_OPENGL_21)
// Open menu effect.
auto open_menu_effect = std::make_shared<OptionListComponent<std::string>>
(mWindow, getHelpStyle(), "OPEN MENU EFFECT", false);
std::vector<std::string> menu_effects;
menu_effects.push_back("scale-up");
menu_effects.push_back("fade-in");
menu_effects.push_back("none");
for (auto it = menu_effects.cbegin(); it != menu_effects.cend(); it++)
open_menu_effect->add(*it, *it, Settings::getInstance()->
getString("OpenMenuEffect") == *it);
s->addWithLabel("OPEN MENU EFFECT", open_menu_effect);
s->addSaveFunc([open_menu_effect] {
bool needReload = false;
if (Settings::getInstance()->getString("OpenMenuEffect") !=
open_menu_effect->getSelected())
needReload = true;
Settings::getInstance()->setString("OpenMenuEffect", open_menu_effect->getSelected());
if (needReload)
ViewController::get()->reloadAll();
});
// Render scanlines for videos in the gamelists using a shader.
auto render_video_scanlines = std::make_shared<SwitchComponent>(mWindow);
render_video_scanlines->setState(Settings::getInstance()->getBool("GamelistVideoScanlines"));
s->addWithLabel("RENDER SCANLINES FOR GAMELIST VIDEOS", render_video_scanlines);
s->addSaveFunc([render_video_scanlines] {
Settings::getInstance()->setBool("GamelistVideoScanlines",
render_video_scanlines->getState()); });
#endif
// Sort folders on top of the gamelists.
auto folders_on_top = std::make_shared<SwitchComponent>(mWindow);
folders_on_top->setState(Settings::getInstance()->getBool("FoldersOnTop"));

View file

@ -38,6 +38,15 @@ GuiSlideshowScreensaverOptions::GuiSlideshowScreensaverOptions(Window* window, c
Settings::getInstance()->setBool("ScreenSaverStretchImages", sss_stretch->getState());
});
#if defined(USE_OPENGL_21)
// Render scanlines using a shader.
auto render_scanlines = std::make_shared<SwitchComponent>(mWindow);
render_scanlines->setState(Settings::getInstance()->getBool("ScreenSaverImageScanlines"));
addWithLabel(row, "RENDER SCANLINES", render_scanlines);
addSaveFunc([render_scanlines] { Settings::getInstance()->
setBool("ScreenSaverImageScanlines", render_scanlines->getState()); });
#endif
// Background audio file.
auto sss_bg_audio_file = std::make_shared<TextComponent>(mWindow, "",
Font::get(FONT_SIZE_SMALL), 0x777777FF);

View file

@ -87,6 +87,22 @@ GuiVideoScreensaverOptions::GuiVideoScreensaverOptions(Window* window, const cha
addSaveFunc([ss_video_audio] { Settings::getInstance()->
setBool("ScreenSaverVideoAudio", ss_video_audio->getState()); });
#if defined(USE_OPENGL_21)
// Render scanlines using a shader.
auto render_scanlines = std::make_shared<SwitchComponent>(mWindow);
render_scanlines->setState(Settings::getInstance()->getBool("ScreenSaverVideoScanlines"));
addWithLabel("RENDER SCANLINES", render_scanlines);
addSaveFunc([render_scanlines] { Settings::getInstance()->
setBool("ScreenSaverVideoScanlines", render_scanlines->getState()); });
// Render blur using a shader.
auto render_blur = std::make_shared<SwitchComponent>(mWindow);
render_blur->setState(Settings::getInstance()->getBool("ScreenSaverVideoBlur"));
addWithLabel("RENDER BLUR", render_blur);
addSaveFunc([render_blur] { Settings::getInstance()->
setBool("ScreenSaverVideoBlur", render_blur->getState()); });
#endif
#if defined(_RPI_)
// Define subtitle font.
auto ss_omx_font_file = std::make_shared<TextComponent>(mWindow, "",

View file

@ -84,6 +84,8 @@ void Settings::setDefaults()
mStringMap["ThemeSet"] = "rbsimple-DE";
mStringMap["UIMode"] = "full";
mStringMap["DefaultSortOrder"] = "filename, ascending";
mStringMap["OpenMenuEffect"] = "scale-up";
mBoolMap["GamelistVideoScanlines"] = true;
mBoolMap["FoldersOnTop"] = true;
mBoolMap["FavoritesFirst"] = true;
mBoolMap["ForceDisableFilters"] = false;
@ -97,17 +99,10 @@ void Settings::setDefaults()
mBoolMap["ScreenSaverControls"] = true;
mStringMap["ScreenSaverBehavior"] = "dim";
// UI settings -> screensaver settings -> video screensaver settings.
mIntMap["ScreenSaverSwapVideoTimeout"] = 25000;
mBoolMap["ScreenSaverStretchVideos"] = false;
#if defined(_RPI_)
mStringMap["ScreenSaverGameInfo"] = "never";
#endif
mBoolMap["ScreenSaverVideoAudio"] = false;
// UI settings -> screensaver settings -> slideshow screensaver settings.
mIntMap["ScreenSaverSwapImageTimeout"] = 10000;
mBoolMap["ScreenSaverStretchImages"] = false;
mBoolMap["ScreenSaverImageScanlines"] = true;
mStringMap["SlideshowScreenSaverBackgroundAudioFile"] = Utils::FileSystem::getHomePath() +
"/.emulationstation/slideshow/audio/slideshow_bg.wav";
mBoolMap["SlideshowScreenSaverCustomImageSource"] = false;
@ -116,6 +111,16 @@ void Settings::setDefaults()
mBoolMap["SlideshowScreenSaverRecurse"] = false;
mStringMap["SlideshowScreenSaverImageFilter"] = ".png,.jpg";
// UI settings -> screensaver settings -> video screensaver settings.
mIntMap["ScreenSaverSwapVideoTimeout"] = 25000;
mBoolMap["ScreenSaverStretchVideos"] = false;
#if defined(_RPI_)
mStringMap["ScreenSaverGameInfo"] = "never";
#endif
mBoolMap["ScreenSaverVideoAudio"] = false;
mBoolMap["ScreenSaverVideoScanlines"] = true;
mBoolMap["ScreenSaverVideoBlur"] = false;
// Sound settings.
// The ALSA Audio Card and Audio Device selection code is disabled at the moment.
// As PulseAudio controls the sound devices for the desktop environment, it doesn't

View file

@ -9,7 +9,6 @@
#include "components/HelpComponent.h"
#include "components/ImageComponent.h"
#include "resources/Font.h"
#include "resources/TextureResource.h"
#include "InputManager.h"
#include "Log.h"
#include "Scripting.h"
@ -28,7 +27,12 @@ Window::Window()
mScreenSaver(nullptr),
mRenderScreenSaver(false),
mGameLaunchedState(false),
mInfoPopup(nullptr)
mInfoPopup(nullptr),
mCachedBackground(false),
mSaturationAmount(1.0),
mTopOpacity(0),
mTopScale(0.5),
mDimValue(1.0)
{
mHelp = new HelpComponent(this);
mBackgroundOverlay = new ImageComponent(this);
@ -258,19 +262,67 @@ void Window::render()
bottom->render(transform);
if (bottom != top) {
#if defined(USE_OPENGL_21)
if (!mCachedBackground) {
// Generate a cache texture of the shaded background when opening the menu, which
// will remain valid until the menu is closed. This is way faster than having to
// render the shaders for every frame.
std::shared_ptr<TextureResource> mPostprocessedBackground;
mPostprocessedBackground = TextureResource::get("");
unsigned char* processedTexture = new unsigned char[Renderer::getScreenWidth() *
Renderer::getScreenHeight() * 4];
// Defocus the background using three passes of gaussian blur.
Renderer::shaderParameters blurParameters;
blurParameters.shaderPasses = 3;
Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL |
Renderer::SHADER_BLUR_VERTICAL,
blurParameters, processedTexture);
mPostprocessedBackground->initFromPixels(processedTexture,
Renderer::getScreenWidth(), Renderer::getScreenHeight());
mBackgroundOverlay->setImage(mPostprocessedBackground);
delete[] processedTexture;
mCachedBackground = true;
}
#endif
mBackgroundOverlay->render(transform);
#if defined(USE_OPENGL_21)
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), 0x00000070, 0x00000070);
// Open menu effects (scale-up and fade-in).
if (Settings::getInstance()->getString("OpenMenuEffect") == "scale-up") {
if (mTopScale < 1.0)
mTopScale = Math::clamp(mTopScale+0.07, 0, 1.0);
Vector2f topCenter = top->getCenter();
top->setOrigin({0.5, 0.5});
top->setPosition({topCenter.x(), topCenter.y(), 0});
top->setScale(mTopScale);
}
if (Settings::getInstance()->getString("OpenMenuEffect") == "fade-in") {
// Fade-in menu.
if (mTopOpacity < 255)
mTopOpacity = Math::clamp(mTopOpacity+15, 0, 255);
top->setOpacity(mTopOpacity);
}
#endif
top->render(transform);
}
else {
mCachedBackground = false;
mTopOpacity = 0;
mTopScale = 0.5;
}
}
if (!mRenderedHelpPrompts)
mHelp->render(transform);
if (Settings::getInstance()->getBool("DisplayGPUStatistics") && mFrameDataText) {
Renderer::setMatrix(Transform4x4f::Identity());
mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get());
}
unsigned int screensaverTime = (unsigned int)Settings::getInstance()->getInt("ScreenSaverTime");
// If a game has been launched, reset the screensaver timer when it's been reached as we
// don't want to start the screensaver in the background when running a game.
@ -289,7 +341,7 @@ void Window::render()
mInfoPopup->render(transform);
if (mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) {
if (!isProcessing() && mAllowSleep && (!mScreenSaver || mScreenSaver->allowSleep())) {
if (!isProcessing() && mAllowSleep && (!mScreenSaver)) {
// Go to sleep.
if (mSleeping == false) {
mSleeping = true;
@ -297,6 +349,55 @@ void Window::render()
}
}
}
#if defined(USE_OPENGL_21)
// Shaders for the screensavers.
if (mScreenSaver->isScreenSaverActive()) {
if (Settings::getInstance()->getString("ScreenSaverBehavior") == "video") {
if (mScreenSaver->getVideoCount() > 0) {
if (Settings::getInstance()->getBool("ScreenSaverVideoBlur"))
Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL);
if (Settings::getInstance()->getBool("ScreenSaverVideoScanlines"))
Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);
}
else {
// If there are no videos, render a black screen.
Renderer::shaderParameters blackParameters;
blackParameters.fragmentDimValue = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters);
if (mDimValue > 0.0)
mDimValue = Math::clamp(mDimValue-0.045, 0.0, 1.0);
}
}
else if (Settings::getInstance()->getString("ScreenSaverBehavior") == "slideshow") {
if (Settings::getInstance()->getBool("ScreenSaverImageScanlines"))
Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES);
}
else if (Settings::getInstance()->getString("ScreenSaverBehavior") == "dim") {
Renderer::shaderParameters dimParameters;
dimParameters.fragmentDimValue = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, dimParameters);
if (mDimValue > 0.4)
mDimValue = Math::clamp(mDimValue-0.021, 0.4, 1.0);
dimParameters.fragmentSaturation = mSaturationAmount;
Renderer::shaderPostprocessing(Renderer::SHADER_DESATURATE, dimParameters);
if (mSaturationAmount > 0.0)
mSaturationAmount = Math::clamp(mSaturationAmount-0.035, 0.0, 1.0);
}
else if (Settings::getInstance()->getString("ScreenSaverBehavior") == "black") {
Renderer::shaderParameters blackParameters;
blackParameters.fragmentDimValue = mDimValue;
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters);
if (mDimValue > 0.0)
mDimValue = Math::clamp(mDimValue-0.045, 0.0, 1.0);
}
}
#endif
if (Settings::getInstance()->getBool("DisplayGPUStatistics") && mFrameDataText) {
Renderer::setMatrix(Transform4x4f::Identity());
mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get());
}
}
void Window::normalizeNextUpdate()
@ -473,6 +574,9 @@ bool Window::cancelScreenSaver()
for (auto it = mGuiStack.cbegin(); it != mGuiStack.cend(); it++)
(*it)->onScreenSaverDeactivate();
mSaturationAmount = 1.0;
mDimValue = 1.0;
return true;
}

View file

@ -8,6 +8,7 @@
#ifndef ES_CORE_WINDOW_H
#define ES_CORE_WInDOW_H
#include "resources/TextureResource.h"
#include "HelpPrompt.h"
#include "InputConfig.h"
#include "Settings.h"
@ -40,6 +41,7 @@ public:
virtual FileData* getCurrentGame() = 0;
virtual void launchGame() = 0;
virtual void resetCounts() = 0;
virtual unsigned int getVideoCount() = 0;
};
class InfoPopup
@ -116,6 +118,11 @@ private:
bool mNormalizeNextUpdate;
float mSaturationAmount;
unsigned char mTopOpacity;
float mTopScale;
float mDimValue;
bool mCachedBackground;
bool mAllowSleep;
bool mSleeping;
unsigned int mTimeSinceLastInput;

View file

@ -10,6 +10,8 @@
#include "animations/Animation.h"
#include <functional>
// Useful for simple one-off animations, you can supply the animation's apply(t)
// function directly in the constructor as a lambda.
class LambdaAnimation : public Animation

View file

@ -345,10 +345,6 @@ void ImageComponent::updateColors()
mVertices[1].col = mColorGradientHorizontal ? colorEnd : color;
mVertices[2].col = mColorGradientHorizontal ? color : colorEnd;
mVertices[3].col = colorEnd;
mVertices[0].saturation = mSaturation;
mVertices[1].saturation = mSaturation;
mVertices[2].saturation = mSaturation;
mVertices[3].saturation = mSaturation;
}
void ImageComponent::render(const Transform4x4f& parentTrans)
@ -372,7 +368,13 @@ void ImageComponent::render(const Transform4x4f& parentTrans)
// texture is bound in this case but we want to handle a fade so it doesn't just
// 'jump' in when it finally loads.
fadeIn(mTexture->bind());
Renderer::drawTriangleStrips(&mVertices[0], 4);
#if defined(USE_OPENGL_21)
if (mSaturation < 1.0) {
mVertices[0].shaders = Renderer::SHADER_DESATURATE;
mVertices[0].saturation = mSaturation;
}
#endif
Renderer::drawTriangleStrips(&mVertices[0], 4, trans);
}
else {
LOG(LogError) << "Image texture is not initialized!";

View file

@ -171,8 +171,16 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans)
mContext.surface->w, mContext.surface->h);
mTexture->bind();
#if defined(USE_OPENGL_21)
// Render scanlines if this option is enabled. However, if this is the video
// screensaver, then skip this as screensaver scanline rendering is handled from
// Window.cpp as a postprocessing step.
if (!mScreensaverMode && Settings::getInstance()->getBool("GamelistVideoScanlines"))
vertices[0].shaders = Renderer::SHADER_SCANLINES;
#endif
// Render it.
Renderer::drawTriangleStrips(&vertices[0], 4);
Renderer::drawTriangleStrips(&vertices[0], 4, trans);
}
else {
VideoComponent::renderSnapshot(parentTrans);