From 3f336808126e7378dfcdae2aedada58cc151374d Mon Sep 17 00:00:00 2001 From: Taylor Whatley <1whatleytay@hdsb.ca> Date: Thu, 25 Apr 2019 13:33:47 -0400 Subject: [PATCH] gui: Background image option in Game Select screen (#460) * gui: add background images * gui: add background alpha channel * gui: replace background texture with GLObject --- .gitmodules | 3 ++ src/emulator/config.cpp | 6 +++- src/emulator/glutil/include/glutil/object.h | 11 +++++-- src/emulator/glutil/src/object.cpp | 29 +++++++++++++++--- src/emulator/gui/CMakeLists.txt | 2 +- src/emulator/gui/include/gui/state.h | 4 +++ src/emulator/gui/src/game_selector.cpp | 7 +++++ src/emulator/gui/src/gui.cpp | 34 +++++++++++++++++++++ src/emulator/host/include/host/config.h | 2 ++ src/external/CMakeLists.txt | 3 ++ src/external/stb | 1 + 11 files changed, 92 insertions(+), 10 deletions(-) create mode 160000 src/external/stb diff --git a/.gitmodules b/.gitmodules index abdc1e6a8..f6fe28531 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,6 +27,9 @@ path = src/external/spdlog url = https://github.com/gabime/spdlog.git branch = v1.x +[submodule "src/external/stb"] + path = src/external/stb + url = https://github.com/nothings/stb [submodule "src/external/dirent"] path = src/external/dirent url = https://github.com/tronkko/dirent diff --git a/src/emulator/config.cpp b/src/emulator/config.cpp index 72c370570..0d026356b 100644 --- a/src/emulator/config.cpp +++ b/src/emulator/config.cpp @@ -100,6 +100,8 @@ bool serialize(Config &cfg) { config_file_emit_optional_single(emitter, "log-level", cfg.log_level); config_file_emit_optional_single(emitter, "pref-path", cfg.pref_path); config_file_emit_optional_single(emitter, "wait-for-debugger", cfg.wait_for_debugger); + config_file_emit_optional_single(emitter, "background-image", cfg.background_image); + config_file_emit_optional_single(emitter, "background-alpha", cfg.background_alpha); emitter << YAML::EndMap; @@ -118,7 +120,7 @@ static bool deserialize(Config &cfg) { try { config_node = YAML::LoadFile("config.yml"); } catch (YAML::Exception &exception) { - std::cerr << "Config file can't be load: Error: " << exception.what() << "\n"; + std::cerr << "Config file can't be loaded: Error: " << exception.what() << "\n"; return false; } @@ -132,6 +134,8 @@ static bool deserialize(Config &cfg) { get_yaml_value_optional(config_node, "log-level", &cfg.log_level, static_cast(spdlog::level::trace)); get_yaml_value_optional(config_node, "pref-path", &cfg.pref_path); get_yaml_value_optional(config_node, "wait-for-debugger", &cfg.wait_for_debugger); + get_yaml_value_optional(config_node, "background-image", &cfg.background_image); + get_yaml_value_optional(config_node, "background-alpha", &cfg.background_alpha); // lle-modules try { diff --git a/src/emulator/glutil/include/glutil/object.h b/src/emulator/glutil/include/glutil/object.h index 13389edb3..11ea27c4d 100644 --- a/src/emulator/glutil/include/glutil/object.h +++ b/src/emulator/glutil/include/glutil/object.h @@ -24,20 +24,25 @@ class GLObject { public: - using Deleter = std::function; + using SingularDeleter = std::function; + using AggregateDeleter = std::function; GLObject() = default; ~GLObject(); - bool init(GLuint name, Deleter deleter); + bool init(GLuint name, AggregateDeleter aggregate_deleter); + bool init(GLuint name, SingularDeleter singular_deleter); GLuint get() const; operator GLuint() const; + operator bool() const; private: + bool init(GLuint name); const GLObject &operator=(const GLObject &) = delete; GLuint name = 0; - Deleter deleter = nullptr; + AggregateDeleter aggregate_deleter = nullptr; + SingularDeleter singular_deleter = nullptr; }; using SharedGLObject = std::shared_ptr; diff --git a/src/emulator/glutil/src/object.cpp b/src/emulator/glutil/src/object.cpp index b5c6c2af1..679610ed3 100644 --- a/src/emulator/glutil/src/object.cpp +++ b/src/emulator/glutil/src/object.cpp @@ -20,22 +20,37 @@ #include GLObject::~GLObject() { - if (deleter != nullptr) { - deleter(name); + if (aggregate_deleter != nullptr) { + aggregate_deleter(1, &name); + } + if (singular_deleter != nullptr) { + singular_deleter(name); } name = 0; } -bool GLObject::init(GLuint name, Deleter deleter) { +bool GLObject::init(GLuint name) { assert(name != 0); assert(this->name == 0); this->name = name; - if (deleter) - this->deleter = deleter; return name != 0; } +bool GLObject::init(GLuint name, AggregateDeleter aggregate_deleter) { + if (aggregate_deleter) + this->aggregate_deleter = aggregate_deleter; + + return init(name); +} + +bool GLObject::init(GLuint name, SingularDeleter singular_deleter) { + if (singular_deleter) + this->singular_deleter = singular_deleter; + + return init(name); +} + GLuint GLObject::get() const { assert(name != 0); return name; @@ -45,3 +60,7 @@ GLObject::operator GLuint() const { assert(name != 0); return name; } + +GLObject::operator bool() const { + return name; +} diff --git a/src/emulator/gui/CMakeLists.txt b/src/emulator/gui/CMakeLists.txt index bbeb44b3c..4162fcc9b 100644 --- a/src/emulator/gui/CMakeLists.txt +++ b/src/emulator/gui/CMakeLists.txt @@ -23,4 +23,4 @@ src/about_dialog.cpp ) target_include_directories(gui PUBLIC include) -target_link_libraries(gui PUBLIC sdl2 imgui glutil host kernel cpu util dialog) +target_link_libraries(gui PUBLIC sdl2 stb imgui glutil host kernel cpu util dialog) diff --git a/src/emulator/gui/include/gui/state.h b/src/emulator/gui/include/gui/state.h index 4e9d7d795..72b09f59c 100644 --- a/src/emulator/gui/include/gui/state.h +++ b/src/emulator/gui/include/gui/state.h @@ -22,6 +22,8 @@ #include #include +#include + namespace gui { enum SelectorState { @@ -90,6 +92,8 @@ struct State { char disassembly_count[5] = "100"; std::vector disassembly; + GLObject background_texture; + SceUID thread_watch_index = -1; // imgui diff --git a/src/emulator/gui/src/game_selector.cpp b/src/emulator/gui/src/game_selector.cpp index 59937346f..cad30555a 100644 --- a/src/emulator/gui/src/game_selector.cpp +++ b/src/emulator/gui/src/game_selector.cpp @@ -33,10 +33,17 @@ void draw_game_selector(HostState &host) { ImGui::SetNextWindowPos(ImVec2(0, MENUBAR_HEIGHT), ImGuiSetCond_Always); ImGui::SetNextWindowSize(ImVec2(display_size.x, display_size.y - MENUBAR_HEIGHT), ImGuiSetCond_Always); + if (host.cfg.background_alpha) + ImGui::SetNextWindowBgAlpha(host.cfg.background_alpha.value()); ImGui::Begin("Game Selector", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoSavedSettings); static ImGuiTextFilter search_bar; + if (host.gui.background_texture) { + ImGui::GetBackgroundDrawList()->AddImage(reinterpret_cast(host.gui.background_texture.get()), + ImVec2(0, 0), display_size); + } + switch (host.gui.game_selector.state) { case SELECT_APP: ImGui::Columns(3); diff --git a/src/emulator/gui/src/gui.cpp b/src/emulator/gui/src/gui.cpp index 1a3f99fcb..2d3bfebe6 100644 --- a/src/emulator/gui/src/gui.cpp +++ b/src/emulator/gui/src/gui.cpp @@ -29,6 +29,9 @@ #include +#define STB_IMAGE_IMPLEMENTATION +#include + #include #include @@ -120,12 +123,43 @@ static void init_font(State &gui) { gui.normal_font = io.Fonts->AddFontFromMemoryTTF(gui.font_data.data(), font_file_size, 16, &font_config, io.Fonts->GetGlyphRangesJapanese()); } +static void init_background(State &gui, const std::string &image_path) { + if (!std::ifstream(image_path).good()) { + LOG_INFO("Invalid background file path {}.", image_path); + return; + } + + int32_t width = 0; + int32_t height = 0; + stbi_uc *data = stbi_load(image_path.c_str(), &width, &height, nullptr, STBI_rgb_alpha); + + if (!data) { + LOG_INFO("Could not load background from {}.", image_path); + return; + } + + GLuint texture; + glGenTextures(1, &texture); + gui.background_texture.init(texture, glDeleteTextures); + + glBindTexture(GL_TEXTURE_2D, gui.background_texture.get()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +} + void init(HostState &host) { ImGui::CreateContext(); ImGui_ImplSdlGL3_Init(host.window.get()); init_style(); init_font(host.gui); + if (host.cfg.background_image) { + init_background(host.gui, host.cfg.background_image.value()); + } } void draw_begin(HostState &host) { diff --git a/src/emulator/host/include/host/config.h b/src/emulator/host/include/host/config.h index 2e183dd5e..1a1f7a7f7 100644 --- a/src/emulator/host/include/host/config.h +++ b/src/emulator/host/include/host/config.h @@ -40,4 +40,6 @@ struct Config { optional pref_path; bool archive_log = false; optional wait_for_debugger; + optional background_image; + optional background_alpha; }; diff --git a/src/external/CMakeLists.txt b/src/external/CMakeLists.txt index 5b0e48b53..ac44d47eb 100644 --- a/src/external/CMakeLists.txt +++ b/src/external/CMakeLists.txt @@ -54,6 +54,9 @@ target_include_directories(elfio INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/elfio") add_subdirectory(spdlog EXCLUDE_FROM_ALL) target_compile_definitions(spdlog INTERFACE SPDLOG_WCHAR_FILENAMES=1 SPDLOG_NO_THREAD_ID=1 SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) +add_library(stb INTERFACE) +target_include_directories(stb INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/stb") + option(BUILD_SHARED_LIBS "Build shared instead of static libraries." OFF) option(OPTION_BUILD_TOOLS "Build tools." OFF) option(OPTION_BUILD_EXAMPLES "Build examples." OFF) diff --git a/src/external/stb b/src/external/stb new file mode 160000 index 000000000..2c2908f50 --- /dev/null +++ b/src/external/stb @@ -0,0 +1 @@ +Subproject commit 2c2908f50515dcd939f24be261c3ccbcd277bb49