From 55760aee4d7db0a62cd3fe39abe723e45e4527ce Mon Sep 17 00:00:00 2001 From: Alexis Maiquez Date: Tue, 7 Feb 2023 04:17:51 +0100 Subject: [PATCH] More code cleanup: - Separated program args parsing and validation from initialization code - Application's main body in it's own class Signed-off-by: Alexis Maiquez --- CMakeLists.txt | 5 + main.cpp | 459 +----------------- .../Application/CApplicationContext.cpp | 199 ++++++++ .../Application/CApplicationContext.h | 34 ++ .../Application/CWallpaperApplication.cpp | 265 ++++++++++ .../Application/CWallpaperApplication.h | 31 ++ .../Assets/CCombinedContainer.cpp | 23 + .../Assets/CCombinedContainer.h | 6 +- .../Render/Objects/Effects/CPass.cpp | 97 ++-- .../Render/Objects/Effects/CPass.h | 3 + 10 files changed, 623 insertions(+), 499 deletions(-) create mode 100644 src/WallpaperEngine/Application/CApplicationContext.cpp create mode 100644 src/WallpaperEngine/Application/CApplicationContext.h create mode 100644 src/WallpaperEngine/Application/CWallpaperApplication.cpp create mode 100644 src/WallpaperEngine/Application/CWallpaperApplication.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d70a08d..2b2f994 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,11 @@ add_executable( src/WallpaperEngine/Logging/CLog.cpp src/WallpaperEngine/Logging/CLog.h + src/WallpaperEngine/Application/CApplicationContext.cpp + src/WallpaperEngine/Application/CApplicationContext.h + src/WallpaperEngine/Application/CWallpaperApplication.cpp + src/WallpaperEngine/Application/CWallpaperApplication.h + src/WallpaperEngine/Assets/CPackageLoadException.cpp src/WallpaperEngine/Assets/CPackageLoadException.h src/WallpaperEngine/Assets/CAssetLoadException.cpp diff --git a/main.cpp b/main.cpp index 7c89adb..9787084 100644 --- a/main.cpp +++ b/main.cpp @@ -23,227 +23,21 @@ #include "WallpaperEngine/Assets/CPackageLoadException.h" #include "Steam/FileSystem/FileSystem.h" +#include "WallpaperEngine/Application/CApplicationContext.h" +#include "WallpaperEngine/Application/CWallpaperApplication.h" #include "WallpaperEngine/Audio/CAudioContext.h" #include "WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h" #include "WallpaperEngine/Render/Drivers/COpenGLDriver.h" #include "common.h" -#define WORKSHOP_APP_ID 431960 -#define APP_DIRECTORY "wallpaper_engine" - -float g_Time; -float g_TimeLast; -bool g_KeepRunning = true; -bool g_AudioEnabled = true; -int g_AudioVolume = 128; - -void print_help (const char* route) -{ - sLog.out ("Usage: ", route, " [options] background_path/background_id"); - sLog.out (""); - sLog.out ("where background_path/background_id can be:"); - sLog.out ("\tthe ID of the background (for autodetection on your steam installation)"); - sLog.out ("\ta full path to the background's folder"); - sLog.out (""); - sLog.out ("options:"); - sLog.out ("\t--silent\t\t\t\t\tMutes all the sound the wallpaper might produce"); - sLog.out ("\t--volume \t\t\tSets the volume for all the sounds in the background"); - sLog.out ("\t--screen-root \tDisplay as screen's background"); - sLog.out ("\t--fps \t\t\tLimits the FPS to the given number, useful to keep battery consumption low"); - sLog.out ("\t--assets-dir \t\t\tFolder where the assets are stored"); - sLog.out ("\t--screenshot\t\t\t\tTakes a screenshot of the background"); - sLog.out ("\t--list-properties\t\t\tList all the available properties and their possible values"); - sLog.out ("\t--set-property \tOverrides the default value of the given property"); -} - -std::string stringPathFixes(const std::string& s) -{ - if (s.empty () == true) - return s; - - std::string str (s); - - // remove single-quotes from the arguments - if (str [0] == '\'' && str [str.size() - 1] == '\'') - str - .erase (str.size() - 1, 1) - .erase (0, 1); - - return std::move (str); -} +WallpaperEngine::Application::CWallpaperApplication* appPointer; void signalhandler(int sig) { - g_KeepRunning = false; -} + if (appPointer == nullptr) + return; -void addPkg (CCombinedContainer* containers, const std::filesystem::path& path, std::string pkgfile) -{ - try - { - auto scene_path = std::filesystem::path (path) / pkgfile; - - // add the package to the list - containers->add (new WallpaperEngine::Assets::CPackage (scene_path)); - sLog.out ("Detected ", pkgfile, " file at ", scene_path, ". Adding to list of searchable paths"); - } - catch (CPackageLoadException& ex) - { - // ignore this error, the package file was not found - sLog.out ("No ", pkgfile, " file found at ", path, ". Defaulting to normal folder storage"); - } - catch (std::runtime_error& ex) - { - // the package was found but there was an error loading it (wrong header or something) - sLog.exception ("Failed to load scene.pkg file: ", ex.what()); - } -} - -CVirtualContainer* buildVirtualContainer () -{ - CVirtualContainer* container = new WallpaperEngine::Assets::CVirtualContainer (); - - // - // Had to get a little creative with the effects to achieve the same bloom effect without any custom code - // these virtual files are loaded by an image in the scene that takes current _rt_FullFrameBuffer and - // applies the bloom effect to render it out to the screen - // - - // add the effect file for screen bloom - container->add ( - "effects/wpenginelinux/bloomeffect.json", - "{" - "\t\"name\":\"camerabloom_wpengine_linux\"," - "\t\"group\":\"wpengine_linux_camera\"," - "\t\"dependencies\":[]," - "\t\"passes\":" - "\t[" - "\t\t{" - "\t\t\t\"material\": \"materials/util/downsample_quarter_bloom.json\"," - "\t\t\t\"target\": \"_rt_4FrameBuffer\"," - "\t\t\t\"bind\":" - "\t\t\t[" - "\t\t\t\t{" - "\t\t\t\t\t\"name\": \"_rt_FullFrameBuffer\"," - "\t\t\t\t\t\"index\": 0" - "\t\t\t\t}" - "\t\t\t]" - "\t\t}," - "\t\t{" - "\t\t\t\"material\": \"materials/util/downsample_eighth_blur_v.json\"," - "\t\t\t\"target\": \"_rt_8FrameBuffer\"," - "\t\t\t\"bind\":" - "\t\t\t[" - "\t\t\t\t{" - "\t\t\t\t\t\"name\": \"_rt_4FrameBuffer\"," - "\t\t\t\t\t\"index\": 0" - "\t\t\t\t}" - "\t\t\t]" - "\t\t}," - "\t\t{" - "\t\t\t\"material\": \"materials/util/blur_h_bloom.json\"," - "\t\t\t\"target\": \"_rt_Bloom\"," - "\t\t\t\"bind\":" - "\t\t\t[" - "\t\t\t\t{" - "\t\t\t\t\t\"name\": \"_rt_8FrameBuffer\"," - "\t\t\t\t\t\"index\": 0" - "\t\t\t\t}" - "\t\t\t]" - "\t\t}," - "\t\t{" - "\t\t\t\"material\": \"materials/util/combine.json\"," - "\t\t\t\"target\": \"_rt_FullFrameBuffer\"," - "\t\t\t\"bind\":" - "\t\t\t[" - "\t\t\t\t{" - "\t\t\t\t\t\"name\": \"_rt_imageLayerComposite_-1_a\"," - "\t\t\t\t\t\"index\": 0" - "\t\t\t\t}," - "\t\t\t\t{" - "\t\t\t\t\t\"name\": \"_rt_Bloom\"," - "\t\t\t\t\t\"index\": 1" - "\t\t\t\t}" - "\t\t\t]" - "\t\t}" - "\t]" - "}" - ); - - // add some model for the image element even if it's going to waste rendering cycles - container->add ( - "models/wpenginelinux.json", - "{" - "\t\"material\":\"materials/wpenginelinux.json\"" - "}" - ); - - // models require materials, so add that too - container->add ( - "materials/wpenginelinux.json", - "{" - "\t\"passes\":" - "\t\t[" - "\t\t\t{" - "\t\t\t\t\"blending\": \"normal\"," - "\t\t\t\t\"cullmode\": \"nocull\"," - "\t\t\t\t\"depthtest\": \"disabled\"," - "\t\t\t\t\"depthwrite\": \"disabled\"," - "\t\t\t\t\"shader\": \"genericimage2\"," - "\t\t\t\t\"textures\": [\"_rt_FullFrameBuffer\"]" - "\t\t\t}" - "\t\t]" - "}" - ); - - return container; -} - -void takeScreenshot (WallpaperEngine::Render::CWallpaper* wp, const std::filesystem::path& filename, FREE_IMAGE_FORMAT format) -{ - GLint width, height; - - // bind texture and get the size - glBindFramebuffer (GL_FRAMEBUFFER, wp->getWallpaperFramebuffer ()); - glBindTexture (GL_TEXTURE_2D, wp->getWallpaperTexture ()); - glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); - glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); - - // make room for storing the pixel data - uint8_t* buffer = new uint8_t [width * height * sizeof (uint8_t) * 3]; - uint8_t* pixel = buffer; - - // read the image into the buffer - glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer); - - // build the output file with FreeImage - FIBITMAP* bitmap = FreeImage_Allocate (width, height, 24); - RGBQUAD color; - - // now get access to the pixels - for (int y = height; y > 0; y --) - { - for (int x = 0; x < width; x ++) - { - color.rgbRed = *pixel ++; - color.rgbGreen = *pixel ++; - color.rgbBlue = *pixel ++; - - // set the pixel in the destination - FreeImage_SetPixelColor (bitmap, x, y, &color); - } - } - - // finally save the file - FreeImage_Save (format, bitmap, filename.c_str (), 0); - - // free all the used memory - delete[] buffer; - - FreeImage_Unload (bitmap); - - // unbind the textures - glBindTexture (GL_TEXTURE_2D, GL_NONE); + appPointer->signal (sig); } void initLogging () @@ -256,244 +50,21 @@ int main (int argc, char* argv[]) { initLogging (); - std::vector screens; - std::map propertyOverrides; - - int maximumFPS = 30; - bool shouldTakeScreenshot = false; - bool shouldListPropertiesAndStop = false; - FREE_IMAGE_FORMAT screenshotFormat = FIF_UNKNOWN; - std::string path; - std::filesystem::path assetsPath; - std::filesystem::path screenshotPath; - - static struct option long_options [] = { - {"screen-root", required_argument, 0, 'r'}, - {"pkg", required_argument, 0, 'p'}, - {"dir", required_argument, 0, 'd'}, - {"silent", no_argument, 0, 's'}, - {"volume", required_argument, 0, 'v'}, - {"help", no_argument, 0, 'h'}, - {"fps", required_argument, 0, 'f'}, - {"assets-dir", required_argument, 0, 'a'}, - {"screenshot", required_argument, 0, 'c'}, - {"list-properties", no_argument, 0, 'l'}, - {"set-property", required_argument, 0, 'o'}, - {nullptr, 0, 0, 0} - }; - - while (true) - { - int c = getopt_long (argc, argv, "r:p:d:shf:a:", long_options, nullptr); - - if (c == -1) - break; - - switch (c) - { - case 'o': - { - std::string value = optarg; - std::string::size_type equals = value.find ('='); - - // properties without value are treated as booleans for now - if (equals == std::string::npos) - propertyOverrides.insert_or_assign (value, "1"); - else - propertyOverrides.insert_or_assign ( - value.substr (0, equals), - value.substr (equals + 1) - ); - } - break; - - case 'l': - shouldListPropertiesAndStop = true; - break; - - case 'r': - screens.emplace_back (optarg); - break; - - case 'p': - case 'd': - sLog.error ("--dir/--pkg is deprecated and not used anymore"); - path = stringPathFixes (optarg); - break; - - case 's': - g_AudioEnabled = false; - break; - - case 'h': - print_help (argv [0]); - break; - - case 'f': - maximumFPS = atoi (optarg); - break; - - case 'a': assetsPath = optarg; - break; - - case 'v': - g_AudioVolume = atoi (optarg); - break; - - case 'c': - shouldTakeScreenshot = true; - screenshotPath = stringPathFixes (optarg); - break; - } - } - - if (path.empty () == true) - { - if (optind < argc && strlen (argv [optind]) > 0) - { - path = argv [optind]; - } - else - { - print_help (argv [0]); - return 0; - } - } - - // validate screenshot file just to make sure - if (shouldTakeScreenshot == true) - { - if (screenshotPath.has_extension () == false) - sLog.exception ("Cannot determine screenshot format"); - - std::string extension = screenshotPath.extension (); - - if (extension == ".bmp") - screenshotFormat = FIF_BMP; - else if (extension == ".png") - screenshotFormat = FIF_PNG; - else if (extension == ".jpg" || extension == ".jpeg") - screenshotFormat = FIF_JPEG; - else - sLog.exception ("Cannot determine screenshot format, unknown extension ", extension); - } - - // check if the background might be an ID and try to find the right path in the steam installation folder - if (path.find ('/') == std::string::npos) - path = Steam::FileSystem::workshopDirectory (WORKSHOP_APP_ID, path); - - WallpaperEngine::Assets::CCombinedContainer containers; - // the background's path is required to load project.json regardless of the type of background we're using - containers.add (new WallpaperEngine::Assets::CDirectory (path)); - // add the virtual container for mocked up files - containers.add (buildVirtualContainer ()); - // try to add the common packages - addPkg (&containers, path, "scene.pkg"); - addPkg (&containers, path, "gifscene.pkg"); - - if (assetsPath.empty () == true) - { - try - { - assetsPath = Steam::FileSystem::appDirectory (APP_DIRECTORY, "assets"); - } - catch (std::runtime_error) - { - // set current path as assets' folder - std::filesystem::path directory = std::filesystem::canonical ("/proc/self/exe") - .parent_path () / "assets"; - } - } - else - { - sLog.out ("Found wallpaper engine's assets at ", assetsPath, " based on --assets-dir parameter"); - } - - if (assetsPath.empty () == true) - sLog.exception ("Cannot determine a valid path for the wallpaper engine assets"); - - // add containers to the list - containers.add (new WallpaperEngine::Assets::CDirectory (assetsPath)); - - // parse the project.json file - auto project = WallpaperEngine::Core::CProject::fromFile ("project.json", &containers); - // go to the right folder so the videos will play - if (project->getWallpaper ()->is () == true) - chdir (path.c_str ()); - - // show properties if required - for (auto cur : project->getProperties ()) - { - // update the value of the property - auto override = propertyOverrides.find (cur->getName ()); - - if (override != propertyOverrides.end ()) - { - sLog.out ("Applying override value for ", cur->getName ()); - - cur->update (override->second); - } - - if (shouldListPropertiesAndStop) - sLog.out (cur->dump ()); - } + WallpaperEngine::Application::CApplicationContext appContext (argc, argv); + WallpaperEngine::Application::CWallpaperApplication app (appContext); // halt if the list-properties option was specified - if (shouldListPropertiesAndStop) + if (appContext.onlyListProperties) return 0; - // attach signals so if a stop is requested the X11 resources are freed and the program shutsdown gracefully - std::signal(SIGINT, signalhandler); - std::signal(SIGTERM, signalhandler); + appPointer = &app; - // initialize sdl audio driver - WallpaperEngine::Audio::Drivers::CSDLAudioDriver audioDriver; - // initialize audio context - WallpaperEngine::Audio::CAudioContext audioContext (&audioDriver); - // initialize OpenGL driver - WallpaperEngine::Render::Drivers::COpenGLDriver videoDriver ("Wallpaper Engine"); - // initialize render context - WallpaperEngine::Render::CRenderContext context (screens, videoDriver, &containers); - // initialize mouse support - context.setMouse (new CMouseInput (videoDriver.getWindow ())); - // ensure the context knows what wallpaper to render - context.setWallpaper ( - WallpaperEngine::Render::CWallpaper::fromWallpaper (project->getWallpaper (), &context, &audioContext) - ); + // attach signals to gracefully stop + std::signal (SIGINT, signalhandler); + std::signal (SIGTERM, signalhandler); - float startTime, endTime, minimumTime = 1.0f / maximumFPS; - - while (videoDriver.closeRequested () == false && g_KeepRunning == true) - { - // keep track of the previous frame's time - g_TimeLast = g_Time; - // calculate the current time value - g_Time = videoDriver.getRenderTime (); - // get the start time of the frame - startTime = g_Time; - // render the scene - context.render (); - // get the end time of the frame - endTime = videoDriver.getRenderTime (); - - // ensure the frame time is correct to not overrun FPS - if ((endTime - startTime) < minimumTime) - usleep ((minimumTime - (endTime - startTime)) * CLOCKS_PER_SEC); - - if (shouldTakeScreenshot == true && videoDriver.getFrameCounter () == 5) - { - takeScreenshot (context.getWallpaper (), screenshotPath, screenshotFormat); - // disable screenshot just in case the counter overflows - shouldTakeScreenshot = false; - } - } - - // ensure this is updated as sometimes it might not come from a signal - g_KeepRunning = false; - - sLog.out ("Stop requested"); - - SDL_Quit (); + // show the wallpaper application + app.show (); return 0; } \ No newline at end of file diff --git a/src/WallpaperEngine/Application/CApplicationContext.cpp b/src/WallpaperEngine/Application/CApplicationContext.cpp new file mode 100644 index 0000000..ebe2c14 --- /dev/null +++ b/src/WallpaperEngine/Application/CApplicationContext.cpp @@ -0,0 +1,199 @@ +#include "CApplicationContext.h" + +#include "Steam/FileSystem/FileSystem.h" +#include "WallpaperEngine/Logging/CLog.h" + +#include +#include +#include + +#define WORKSHOP_APP_ID 431960 +#define APP_DIRECTORY "wallpaper_engine" + +using namespace WallpaperEngine::Application; + +struct option long_options [] = { + {"screen-root", required_argument, 0, 'r'}, + {"pkg", required_argument, 0, 'p'}, + {"dir", required_argument, 0, 'd'}, + {"silent", no_argument, 0, 's'}, + {"volume", required_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"fps", required_argument, 0, 'f'}, + {"assets-dir", required_argument, 0, 'a'}, + {"screenshot", required_argument, 0, 'c'}, + {"list-properties", no_argument, 0, 'l'}, + {"set-property", required_argument, 0, 'o'}, + {nullptr, 0, 0, 0} +}; + +std::string stringPathFixes(const std::string& s) +{ + if (s.empty () == true) + return s; + + std::string str (s); + + // remove single-quotes from the arguments + if (str [0] == '\'' && str [str.size() - 1] == '\'') + str + .erase (str.size() - 1, 1) + .erase (0, 1); + + return std::move (str); +} + +CApplicationContext::CApplicationContext (int argc, char* argv[]) : + takeScreenshot (false), + maximumFPS (30), + audioVolume (128), + audioEnabled (true), + onlyListProperties (false) +{ + int c; + + while ((c = getopt_long (argc, argv, "r:p:d:shf:a:", long_options, nullptr)) != -1) + { + switch (c) + { + case 'o': + { + std::string value = optarg; + std::string::size_type equals = value.find ('='); + + // properties without value are treated as booleans for now + if (equals == std::string::npos) + this->properties.insert_or_assign (value, "1"); + else + this->properties.insert_or_assign ( + value.substr (0, equals), + value.substr (equals + 1) + ); + } + break; + + case 'l': + this->onlyListProperties = true; + break; + + case 'r': + screens.emplace_back (optarg); + break; + + case 'p': + case 'd': + sLog.error ("--dir/--pkg is deprecated and not used anymore"); + this->background = stringPathFixes (optarg); + break; + + case 's': + this->audioEnabled = false; + break; + + case 'h': + this->printHelp (argv [0]); + break; + + case 'f': + maximumFPS = atoi (optarg); + break; + + case 'a': + this->assets = stringPathFixes (optarg); + break; + + case 'v': + this->audioVolume = std::max (atoi (optarg), 128); + break; + + case 'c': + this->takeScreenshot = true; + this->screenshot = stringPathFixes (optarg); + break; + } + } + + if (this->background.empty () == true) + { + if (optind < argc && strlen (argv [optind]) > 0) + { + this->background = argv [optind]; + } + else + { + this->printHelp (argv [0]); + } + } + + // perform some extra validation on the inputs + this->validatePath (); + this->validateAssets (); + this->validateScreenshot (); +} + +void CApplicationContext::validatePath () +{ + if (this->background.find ('/') != std::string::npos) + return; + + this->background = Steam::FileSystem::workshopDirectory (WORKSHOP_APP_ID, this->background); +} + +void CApplicationContext::validateAssets () +{ + if (this->assets.empty () == false) + { + sLog.out ("Using wallpaper engine's assets at ", this->assets, " based on --assets-dir parameter"); + return; + } + + try + { + this->assets = Steam::FileSystem::appDirectory (APP_DIRECTORY, "assets"); + } + catch (std::runtime_error&) + { + // set current path as assets' folder + std::filesystem::path directory = std::filesystem::canonical ("/proc/self/exe") + .parent_path () / "assets"; + } +} + +void CApplicationContext::validateScreenshot () +{ + if (this->takeScreenshot == false) + return; + + if (this->screenshot.has_extension () == false) + sLog.exception ("Cannot determine screenshot format"); + + std::string extension = this->screenshot.extension (); + + if (extension == ".bmp") + this->screenshotFormat = FIF_BMP; + else if (extension == ".png") + this->screenshotFormat = FIF_PNG; + else if (extension == ".jpg" || extension == ".jpeg") + this->screenshotFormat = FIF_JPEG; + else + sLog.exception ("Cannot determine screenshot format, unknown extension ", extension); +} + +void CApplicationContext::printHelp (const char* route) +{ + sLog.out ("Usage: ", route, " [options] background_path/background_id"); + sLog.out (""); + sLog.out ("where background_path/background_id can be:"); + sLog.out ("\tthe ID of the background (for autodetection on your steam installation)"); + sLog.out ("\ta full path to the background's folder"); + sLog.out (""); + sLog.out ("options:"); + sLog.out ("\t--silent\t\t\t\t\tMutes all the sound the wallpaper might produce"); + sLog.out ("\t--volume \t\t\tSets the volume for all the sounds in the background"); + sLog.out ("\t--screen-root \tDisplay as screen's background"); + sLog.out ("\t--fps \t\t\tLimits the FPS to the given number, useful to keep battery consumption low"); + sLog.out ("\t--assets-dir \t\t\tFolder where the assets are stored"); + sLog.out ("\t--screenshot\t\t\t\tTakes a screenshot of the background"); + sLog.out ("\t--list-properties\t\t\tList all the available properties and their possible values"); + sLog.out ("\t--set-property \tOverrides the default value of the given property"); +} \ No newline at end of file diff --git a/src/WallpaperEngine/Application/CApplicationContext.h b/src/WallpaperEngine/Application/CApplicationContext.h new file mode 100644 index 0000000..5f2d64f --- /dev/null +++ b/src/WallpaperEngine/Application/CApplicationContext.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace WallpaperEngine::Application +{ + class CApplicationContext + { + public: + CApplicationContext (int argc, char* argv[]); + + std::vector screens; + std::map properties; + std::string background; + std::filesystem::path assets; + std::filesystem::path screenshot; + bool takeScreenshot; + int maximumFPS; + int audioVolume; + bool audioEnabled; + bool onlyListProperties; + FREE_IMAGE_FORMAT screenshotFormat; + + private: + void validatePath (); + void validateAssets (); + void validateScreenshot (); + void printHelp (const char* route); + }; +} \ No newline at end of file diff --git a/src/WallpaperEngine/Application/CWallpaperApplication.cpp b/src/WallpaperEngine/Application/CWallpaperApplication.cpp new file mode 100644 index 0000000..82df30c --- /dev/null +++ b/src/WallpaperEngine/Application/CWallpaperApplication.cpp @@ -0,0 +1,265 @@ +#include "CWallpaperApplication.h" +#include "WallpaperEngine/Assets/CDirectory.h" +#include "WallpaperEngine/Assets/CVirtualContainer.h" +#include "WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h" +#include "WallpaperEngine/Core/CVideo.h" +#include "WallpaperEngine/Logging/CLog.h" +#include "WallpaperEngine/Render/CRenderContext.h" +#include "WallpaperEngine/Render/Drivers/COpenGLDriver.h" + +#include + +float g_Time; +float g_TimeLast; +bool g_KeepRunning = true; +bool g_AudioEnabled = true; +int g_AudioVolume = 128; + +using namespace WallpaperEngine::Application; + +CWallpaperApplication::CWallpaperApplication (CApplicationContext& context) : + m_context (context) +{ + this->setupContainer (); + this->loadProject (); + this->setupProperties (); +} + +void CWallpaperApplication::setupContainer () +{ + this->m_vfs.add (new CDirectory (this->m_context.background)); + this->m_vfs.addPkg (std::filesystem::path (this->m_context.background) / "scene.pkg"); + this->m_vfs.addPkg (std::filesystem::path (this->m_context.background) / "gifscene.pkg"); + this->m_vfs.add (new CDirectory (this->m_context.assets)); + + // TODO: move this somewhere else? + CVirtualContainer* container = new CVirtualContainer (); + + // + // Had to get a little creative with the effects to achieve the same bloom effect without any custom code + // these virtual files are loaded by an image in the scene that takes current _rt_FullFrameBuffer and + // applies the bloom effect to render it out to the screen + // + + // add the effect file for screen bloom + container->add ( + "effects/wpenginelinux/bloomeffect.json", + "{" + "\t\"name\":\"camerabloom_wpengine_linux\"," + "\t\"group\":\"wpengine_linux_camera\"," + "\t\"dependencies\":[]," + "\t\"passes\":" + "\t[" + "\t\t{" + "\t\t\t\"material\": \"materials/util/downsample_quarter_bloom.json\"," + "\t\t\t\"target\": \"_rt_4FrameBuffer\"," + "\t\t\t\"bind\":" + "\t\t\t[" + "\t\t\t\t{" + "\t\t\t\t\t\"name\": \"_rt_FullFrameBuffer\"," + "\t\t\t\t\t\"index\": 0" + "\t\t\t\t}" + "\t\t\t]" + "\t\t}," + "\t\t{" + "\t\t\t\"material\": \"materials/util/downsample_eighth_blur_v.json\"," + "\t\t\t\"target\": \"_rt_8FrameBuffer\"," + "\t\t\t\"bind\":" + "\t\t\t[" + "\t\t\t\t{" + "\t\t\t\t\t\"name\": \"_rt_4FrameBuffer\"," + "\t\t\t\t\t\"index\": 0" + "\t\t\t\t}" + "\t\t\t]" + "\t\t}," + "\t\t{" + "\t\t\t\"material\": \"materials/util/blur_h_bloom.json\"," + "\t\t\t\"target\": \"_rt_Bloom\"," + "\t\t\t\"bind\":" + "\t\t\t[" + "\t\t\t\t{" + "\t\t\t\t\t\"name\": \"_rt_8FrameBuffer\"," + "\t\t\t\t\t\"index\": 0" + "\t\t\t\t}" + "\t\t\t]" + "\t\t}," + "\t\t{" + "\t\t\t\"material\": \"materials/util/combine.json\"," + "\t\t\t\"target\": \"_rt_FullFrameBuffer\"," + "\t\t\t\"bind\":" + "\t\t\t[" + "\t\t\t\t{" + "\t\t\t\t\t\"name\": \"_rt_imageLayerComposite_-1_a\"," + "\t\t\t\t\t\"index\": 0" + "\t\t\t\t}," + "\t\t\t\t{" + "\t\t\t\t\t\"name\": \"_rt_Bloom\"," + "\t\t\t\t\t\"index\": 1" + "\t\t\t\t}" + "\t\t\t]" + "\t\t}" + "\t]" + "}" + ); + + // add some model for the image element even if it's going to waste rendering cycles + container->add ( + "models/wpenginelinux.json", + "{" + "\t\"material\":\"materials/wpenginelinux.json\"" + "}" + ); + + // models require materials, so add that too + container->add ( + "materials/wpenginelinux.json", + "{" + "\t\"passes\":" + "\t\t[" + "\t\t\t{" + "\t\t\t\t\"blending\": \"normal\"," + "\t\t\t\t\"cullmode\": \"nocull\"," + "\t\t\t\t\"depthtest\": \"disabled\"," + "\t\t\t\t\"depthwrite\": \"disabled\"," + "\t\t\t\t\"shader\": \"genericimage2\"," + "\t\t\t\t\"textures\": [\"_rt_FullFrameBuffer\"]" + "\t\t\t}" + "\t\t]" + "}" + ); + + this->m_vfs.add (container); +} + +void CWallpaperApplication::loadProject () +{ + this->m_project = CProject::fromFile ("project.json", &this->m_vfs); + // go to the right folder so the videos will play + // TODO: stop doing chdir and use full path + if (this->m_project->getWallpaper ()->is () == true) + chdir (this->m_context.background.c_str ()); +} + +void CWallpaperApplication::setupProperties () +{ + // show properties if required + for (auto cur : this->m_project->getProperties ()) + { + // update the value of the property + auto override = this->m_context.properties.find (cur->getName ()); + + if (override != this->m_context.properties.end ()) + { + sLog.out ("Applying override value for ", cur->getName ()); + + cur->update (override->second); + } + + if (this->m_context.onlyListProperties) + sLog.out (cur->dump ()); + } +} + +void CWallpaperApplication::takeScreenshot (WallpaperEngine::Render::CWallpaper* wp, const std::filesystem::path& filename, FREE_IMAGE_FORMAT format) +{ + GLint width, height; + + // bind texture and get the size + glBindFramebuffer (GL_FRAMEBUFFER, wp->getWallpaperFramebuffer ()); + glBindTexture (GL_TEXTURE_2D, wp->getWallpaperTexture ()); + glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + + // make room for storing the pixel data + uint8_t* buffer = new uint8_t [width * height * sizeof (uint8_t) * 3]; + uint8_t* pixel = buffer; + + // read the image into the buffer + glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer); + + // build the output file with FreeImage + FIBITMAP* bitmap = FreeImage_Allocate (width, height, 24); + RGBQUAD color; + + // now get access to the pixels + for (int y = height; y > 0; y --) + { + for (int x = 0; x < width; x ++) + { + color.rgbRed = *pixel ++; + color.rgbGreen = *pixel ++; + color.rgbBlue = *pixel ++; + + // set the pixel in the destination + FreeImage_SetPixelColor (bitmap, x, y, &color); + } + } + + // finally save the file + FreeImage_Save (format, bitmap, filename.c_str (), 0); + + // free all the used memory + delete[] buffer; + + FreeImage_Unload (bitmap); + + // unbind the textures + glBindTexture (GL_TEXTURE_2D, GL_NONE); +} + +void CWallpaperApplication::show () +{ + // initialize sdl audio driver + WallpaperEngine::Audio::Drivers::CSDLAudioDriver audioDriver; + // initialize audio context + WallpaperEngine::Audio::CAudioContext audioContext (&audioDriver); + // initialize OpenGL driver + WallpaperEngine::Render::Drivers::COpenGLDriver videoDriver (this->m_project->getTitle ().c_str ()); + // initialize render context + WallpaperEngine::Render::CRenderContext context (this->m_context.screens, videoDriver, &this->m_vfs); + // initialize mouse support + context.setMouse (new CMouseInput (videoDriver.getWindow ())); + // ensure the context knows what wallpaper to render + context.setWallpaper ( + WallpaperEngine::Render::CWallpaper::fromWallpaper (this->m_project->getWallpaper (), &context, &audioContext) + ); + + float startTime, endTime, minimumTime = 1.0f / this->m_context.maximumFPS; + + while (videoDriver.closeRequested () == false && g_KeepRunning == true) + { + // keep track of the previous frame's time + g_TimeLast = g_Time; + // calculate the current time value + g_Time = videoDriver.getRenderTime (); + // get the start time of the frame + startTime = g_Time; + // render the scene + context.render (); + // get the end time of the frame + endTime = videoDriver.getRenderTime (); + + // ensure the frame time is correct to not overrun FPS + if ((endTime - startTime) < minimumTime) + usleep ((minimumTime - (endTime - startTime)) * CLOCKS_PER_SEC); + + if (this->m_context.takeScreenshot == true && videoDriver.getFrameCounter () == 5) + { + this->takeScreenshot (context.getWallpaper (), this->m_context.screenshot, this->m_context.screenshotFormat); + // disable screenshot just in case the counter overflows + this->m_context.takeScreenshot = false; + } + } + + // ensure this is updated as sometimes it might not come from a signal + g_KeepRunning = false; + + sLog.out ("Stop requested"); + + SDL_Quit (); +} + +void CWallpaperApplication::signal (int signal) +{ + g_KeepRunning = false; +} \ No newline at end of file diff --git a/src/WallpaperEngine/Application/CWallpaperApplication.h b/src/WallpaperEngine/Application/CWallpaperApplication.h new file mode 100644 index 0000000..c5e374d --- /dev/null +++ b/src/WallpaperEngine/Application/CWallpaperApplication.h @@ -0,0 +1,31 @@ +#pragma once + +#include "CApplicationContext.h" +#include "WallpaperEngine/Assets/CCombinedContainer.h" +#include "WallpaperEngine/Core/CProject.h" +#include "WallpaperEngine/Render/CWallpaper.h" + +namespace WallpaperEngine::Application +{ + using namespace WallpaperEngine::Core; + using namespace WallpaperEngine::Assets; + + class CWallpaperApplication + { + public: + CWallpaperApplication (CApplicationContext& context); + + void show (); + void signal (int signal); + + private: + void setupContainer (); + void loadProject (); + void setupProperties (); + void takeScreenshot (WallpaperEngine::Render::CWallpaper* wp, const std::filesystem::path& filename, FREE_IMAGE_FORMAT format); + + CProject* m_project; + CApplicationContext& m_context; + CCombinedContainer m_vfs; + }; +} diff --git a/src/WallpaperEngine/Assets/CCombinedContainer.cpp b/src/WallpaperEngine/Assets/CCombinedContainer.cpp index 2664bc5..1a05b35 100644 --- a/src/WallpaperEngine/Assets/CCombinedContainer.cpp +++ b/src/WallpaperEngine/Assets/CCombinedContainer.cpp @@ -1,5 +1,8 @@ #include "CCombinedContainer.h" #include "CAssetLoadException.h" +#include "CPackage.h" +#include "CPackageLoadException.h" +#include "WallpaperEngine/Logging/CLog.h" using namespace WallpaperEngine::Assets; @@ -8,6 +11,26 @@ void CCombinedContainer::add (CContainer* container) this->m_containers.emplace_back (container); } +void CCombinedContainer::addPkg (const std::filesystem::path& path) +{ + try + { + // add the package to the list + this->add (new CPackage (path)); + sLog.out ("Detected ", path.filename (), " file at ", path, ". Adding to list of searchable paths"); + } + catch (CPackageLoadException& ex) + { + // ignore this error, the package file was not found + sLog.out ("No ", path.filename (), " file found at ", path, ". Defaulting to normal folder storage"); + } + catch (std::runtime_error& ex) + { + // the package was found but there was an error loading it (wrong header or something) + sLog.exception ("Failed to load scene.pkg file: ", ex.what()); + } +} + const void* CCombinedContainer::readFile (std::string filename, uint32_t* length) const { for (auto cur : this->m_containers) diff --git a/src/WallpaperEngine/Assets/CCombinedContainer.h b/src/WallpaperEngine/Assets/CCombinedContainer.h index ffe9125..43477d3 100644 --- a/src/WallpaperEngine/Assets/CCombinedContainer.h +++ b/src/WallpaperEngine/Assets/CCombinedContainer.h @@ -1,11 +1,8 @@ -// -// Created by almamu on 8/8/21. -// - #pragma once #include #include +#include #include "CContainer.h" @@ -20,6 +17,7 @@ namespace WallpaperEngine::Assets * @param container */ void add (CContainer* container); + void addPkg (const std::filesystem::path& path); const void* readFile (std::string filename, uint32_t* length) const override; diff --git a/src/WallpaperEngine/Render/Objects/Effects/CPass.cpp b/src/WallpaperEngine/Render/Objects/Effects/CPass.cpp index a0e26f7..cced1d2 100644 --- a/src/WallpaperEngine/Render/Objects/Effects/CPass.cpp +++ b/src/WallpaperEngine/Render/Objects/Effects/CPass.cpp @@ -716,68 +716,53 @@ void CPass::setupShaderVariables () if (vertexVar == nullptr && pixelVar == nullptr) continue; - // if both can be found, ensure they're the correct type - /*if (vertexVar != nullptr && pixelVar != nullptr) - { - if (vertexVar->getType () != pixelVar->getType ()) - throw std::runtime_error ("Pixel and vertex shader variable types do not match"); - }*/ - // get one instance of it CShaderVariable* var = vertexVar == nullptr ? pixelVar : vertexVar; // ensure the shader's and the constant are of the same type - if (cur.second->getType () != var->getType ()) + if (cur.second->getType () == var->getType ()) { - // there's situations where this type mismatch is actually expected - // integers and floats are equivalent, this could be detected at load time - // but that'd mean to compile the shader in the load, and not on the render stage - // so take into account these conversions here + this->addUniform (var->getName (), cur.second); + continue; + } - if (cur.second->is () == true && var->is () == true) - { - // create an integer value from a float - this->addUniform (var->getName (), static_cast (*cur.second->as ()->getValue ())); - } - else if (cur.second->is () == true && var->is () == true) - { - // create a float value from an integer - this->addUniform (var->getName (), static_cast (*cur.second->as ()->getValue ())); - } - else if (cur.second->is () == true && var->is () == true) - { - CShaderConstantVector4* val = cur.second->as (); + // there's situations where this type mismatch is actually expected + // integers and floats are equivalent, this could be detected at load time + // but that'd mean to compile the shader in the load, and not on the render stage + // so take into account these conversions here + if (cur.second->is () == true && var->is () == true) + { + // create an integer value from a float + this->addUniform (var->getName (), static_cast (*cur.second->as ()->getValue ())); + } + else if (cur.second->is () == true && var->is () == true) + { + // create a float value from an integer + this->addUniform (var->getName (), static_cast (*cur.second->as ()->getValue ())); + } + else if (cur.second->is () == true && var->is () == true) + { + CShaderConstantVector4* val = cur.second->as (); - // create a new vector2 with the first two values - this->addUniform (var->getName (), {val->getValue ()->x, val->getValue ()->y}); - } - else if (cur.second->is () == true && var->is () == true) - { - CShaderConstantVector4* val = cur.second->as (); + // create a new vector2 with the first two values + this->addUniform (var->getName (), {val->getValue ()->x, val->getValue ()->y}); + } + else if (cur.second->is () == true && var->is () == true) + { + CShaderConstantVector4* val = cur.second->as (); - this->addUniform (var->getName (), {val->getValue ()->x, val->getValue ()->y, val->getValue ()->z}); - } - else - { - sLog.exception ( - "Constant ", - cur.first, - " type does not match pixel/vertex shader variable and cannot be converted (", - cur.second->getType (), - " to ", - var->getType () - ); - } + this->addUniform (var->getName (), {val->getValue ()->x, val->getValue ()->y, val->getValue ()->z}); } else { - // now determine the constant's type and register the correct uniform for it - if (cur.second->is ()) - this->addUniform (var->getName (), cur.second->as ()->getValue ()); - else if (cur.second->is ()) - this->addUniform (var->getName (), cur.second->as ()->getValue ()); - else if (cur.second->is ()) - this->addUniform (var->getName (), cur.second->as ()->getValue ()); + sLog.exception ( + "Constant ", + cur.first, + " type does not match pixel/vertex shader variable and cannot be converted (", + cur.second->getType (), + " to ", + var->getType () + ); } } @@ -804,6 +789,16 @@ void CPass::addUniform (CShaderVariable* value) else if (value->is ()) this->addUniform (value->getName (), const_cast (reinterpret_cast (value->as ()->getValue ()))); } +void CPass::addUniform (const std::string& name, CShaderConstant* value) +{ + // now determine the constant's type and register the correct uniform for it + if (value->is ()) + this->addUniform (name, value->as ()->getValue ()); + else if (value->is ()) + this->addUniform (name, value->as ()->getValue ()); + else if (value->is ()) + this->addUniform (name, value->as ()->getValue ()); +} void CPass::addUniform (const std::string& name, int value) { diff --git a/src/WallpaperEngine/Render/Objects/Effects/CPass.h b/src/WallpaperEngine/Render/Objects/Effects/CPass.h index e255967..129e84e 100644 --- a/src/WallpaperEngine/Render/Objects/Effects/CPass.h +++ b/src/WallpaperEngine/Render/Objects/Effects/CPass.h @@ -4,6 +4,7 @@ #include #include "WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h" +#include "WallpaperEngine/Core/Objects/Effects/Constants/CShaderConstant.h" #include "WallpaperEngine/Render/Objects/Effects/CMaterial.h" #include "WallpaperEngine/Render/Shaders/Compiler.h" #include "WallpaperEngine/Assets/ITexture.h" @@ -13,6 +14,7 @@ namespace WallpaperEngine::Render::Objects::Effects { using namespace WallpaperEngine::Assets; using namespace WallpaperEngine::Render::Shaders::Variables; + using namespace WallpaperEngine::Core::Objects::Effects::Constants; class CMaterial; @@ -90,6 +92,7 @@ namespace WallpaperEngine::Render::Objects::Effects void setupAttributes (); void addAttribute (const std::string& name, GLint type, GLint elements, const GLuint* value); void addUniform (CShaderVariable* value); + void addUniform (const std::string& name, CShaderConstant* value); void addUniform (const std::string& name, int value); void addUniform (const std::string& name, double value); void addUniform (const std::string& name, float value);