From 287758f15d07cb6e0c19d861d8134124361d2d30 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 29 Jul 2014 12:14:25 -0500 Subject: [PATCH] Add the configuration dialog for post processing configuration options. Only enables the config button when the shader options available to it --- Source/Core/DolphinWX/CMakeLists.txt | 1 + Source/Core/DolphinWX/DolphinWX.vcxproj | 4 +- .../Core/DolphinWX/DolphinWX.vcxproj.filters | 8 +- .../DolphinWX/PostProcessingConfigDiag.cpp | 316 ++++++++++++++++++ .../Core/DolphinWX/PostProcessingConfigDiag.h | 106 ++++++ Source/Core/DolphinWX/VideoConfigDiag.cpp | 13 +- Source/Core/DolphinWX/VideoConfigDiag.h | 9 + 7 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 Source/Core/DolphinWX/PostProcessingConfigDiag.cpp create mode 100644 Source/Core/DolphinWX/PostProcessingConfigDiag.h diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 302a27df28..613041de50 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -62,6 +62,7 @@ set(GUI_SRCS MemoryCards/WiiSaveCrypted.cpp NetWindow.cpp PatchAddEdit.cpp + PostProcessingConfigDiag.cpp SoftwareVideoConfigDialog.cpp TASInputDlg.cpp VideoConfigDiag.cpp diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj index e62e2f2e43..52a13521ce 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj @@ -97,6 +97,7 @@ + @@ -144,6 +145,7 @@ + @@ -228,4 +230,4 @@ - \ No newline at end of file + diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters index b9910845a8..59be1c3188 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters @@ -93,6 +93,9 @@ GUI\Video + + GUI\Video + GUI\Video @@ -217,6 +220,9 @@ GUI\Video + + GUI\Video + GUI\Video @@ -296,4 +302,4 @@ Resources - \ No newline at end of file + diff --git a/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp b/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp new file mode 100644 index 0000000000..031c19f19d --- /dev/null +++ b/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp @@ -0,0 +1,316 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Common/StringUtil.h" + +#include "DolphinWX/PostProcessingConfigDiag.h" + +#include "VideoCommon/RenderBase.h" + +PostProcessingConfigDiag::PostProcessingConfigDiag(wxWindow* parent, const std::string& shader) + : wxDialog(parent, -1, + wxString::Format(_("Post Processing Shader Configuration"))), + m_shader(shader) +{ + // Depending on if we are running already, either use the one from the videobackend + // or generate our own. + if (g_renderer && g_renderer->GetPostProcessor()) + { + m_post_processor = g_renderer->GetPostProcessor()->GetConfig(); + } + else + { + m_post_processor = new PostProcessingShaderConfiguration(); + m_post_processor->LoadShader(m_shader); + } + + // Create our UI classes + const PostProcessingShaderConfiguration::ConfigMap& config_map = m_post_processor->GetOptions(); + for (const auto& it : config_map) + { + if (it.second.m_type == PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL) + { + ConfigGrouping* group = new ConfigGrouping(ConfigGrouping::WidgetType::TYPE_TOGGLE, + it.second.m_gui_name, it.first, it.second.m_dependent_option, + &it.second); + m_config_map[it.first] = group; + } + else + { + ConfigGrouping* group = new ConfigGrouping(ConfigGrouping::WidgetType::TYPE_SLIDER, + it.second.m_gui_name, it.first, it.second.m_dependent_option, + &it.second); + m_config_map[it.first] = group; + } + } + + // Arrange our vectors based on dependency + for (const auto& it : m_config_map) + { + const std::string parent_name = it.second->GetParent(); + if (parent_name.size()) + { + // Since it depends on a different object, push it to a parent's object + m_config_map[parent_name]->AddChild(m_config_map[it.first]); + } + else + { + // It doesn't have a child, just push it to the vector + m_config_groups.push_back(m_config_map[it.first]); + } + } + + // Generate our UI + wxNotebook* const notebook = new wxNotebook(this, -1); + wxPanel* const page_general = new wxPanel(notebook, -1); + wxFlexGridSizer* const szr_general = new wxFlexGridSizer(2, 5, 5); + + // Now let's actually populate our window with our information + bool add_general_page = false; + for (const auto& it : m_config_groups) + { + if (it->HasChildren()) + { + // Options with children get their own tab + wxPanel* const page_option = new wxPanel(notebook, -1); + wxFlexGridSizer* const szr_option = new wxFlexGridSizer(2, 10, 5); + it->GenerateUI(this, page_option, szr_option); + + // Add all the children + for (const auto& child : it->GetChildren()) + { + child->GenerateUI(this, page_option, szr_option); + } + page_option->SetSizerAndFit(szr_option); + notebook->AddPage(page_option, _(it->GetGUIName())); + } + else + { + // Options with no children go in to the general tab + if (!add_general_page) + { + // Make it so it doesn't show up if there aren't any options without children. + add_general_page = true; + } + it->GenerateUI(this, page_general, szr_general); + } + } + + if (add_general_page) + { + page_general->SetSizerAndFit(szr_general); + notebook->InsertPage(0, page_general, _("General")); + } + + // Close Button + wxButton* const btn_close = new wxButton(this, wxID_OK, _("Close")); + btn_close->Bind(wxEVT_BUTTON, &PostProcessingConfigDiag::Event_ClickClose, this); + + Bind(wxEVT_CLOSE_WINDOW, &PostProcessingConfigDiag::Event_Close, this); + + wxBoxSizer* const szr_main = new wxBoxSizer(wxVERTICAL); + szr_main->Add(notebook, 1, wxEXPAND | wxALL, 5); + szr_main->Add(btn_close, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 5); + + SetSizerAndFit(szr_main); + Center(); + SetFocus(); + + UpdateWindowUI(); +} + +PostProcessingConfigDiag::~PostProcessingConfigDiag() +{ + m_post_processor->SaveOptionsConfiguration(); + if (g_renderer && g_renderer->GetPostProcessor()) + m_post_processor = nullptr; + else + delete m_post_processor; +} + +void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDiag* dialog, wxWindow* parent, wxFlexGridSizer* sizer) +{ + if (m_type == WidgetType::TYPE_TOGGLE) + { + m_option_checkbox = new wxCheckBox(parent, wxID_ANY, _(m_gui_name)); + m_option_checkbox->SetValue(m_config_option->m_bool_value); + m_option_checkbox->Bind(wxEVT_CHECKBOX, &PostProcessingConfigDiag::Event_CheckBox, + dialog, wxID_ANY, wxID_ANY, new UserEventData(m_option)); + + sizer->Add(m_option_checkbox); + sizer->AddStretchSpacer(); + } + else + { + size_t vector_size = 0; + if (m_config_option->m_type == PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER) + vector_size = m_config_option->m_integer_values.size(); + else + vector_size = m_config_option->m_float_values.size(); + + wxFlexGridSizer* const szr_values = new wxFlexGridSizer(vector_size + 1, 0, 0); + wxStaticText* const option_static_text = new wxStaticText(parent, wxID_ANY, _(m_gui_name)); + sizer->Add(option_static_text); + + for (size_t i = 0; i < vector_size; ++i) + { + // wxSlider uses a signed integer for it's minimum and maximum values + // This won't work for floats. + // Let's determine how many steps we can take and use that instead. + int steps = 0; + int current_value = 0; + std::string string_value; + if (m_config_option->m_type == PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER) + { + // Find out our range by taking the max subtracting the minimum. + double range = m_config_option->m_integer_max_values[i] - m_config_option->m_integer_min_values[i]; + + // How many steps we have is the range divided by the step interval configured. + // This may not be 100% spot on accurate since developers can have odd stepping intervals set. + // Round up so if it is outside our range, then set it to the minimum or maximum + steps = ceil(range / (double)m_config_option->m_integer_step_values[i]); + + // Default value is just the currently set value here + current_value = m_config_option->m_integer_values[i]; + string_value = std::to_string(m_config_option->m_integer_values[i]); + } + else + { + // Same as above but with floats + float range = m_config_option->m_float_max_values[i] - m_config_option->m_float_min_values[i]; + steps = ceil(range / m_config_option->m_float_step_values[i]); + + // We need to convert our default float value from a float to the nearest step value range + current_value = (m_config_option->m_float_values[i] / m_config_option->m_float_step_values[i]); + string_value = std::to_string(m_config_option->m_float_values[i]); + } + + wxSlider* slider = new wxSlider(parent, wxID_ANY, current_value, 0, steps, + wxDefaultPosition, wxSize(200, -1), wxSL_HORIZONTAL | wxSL_BOTTOM); + wxTextCtrl* text_ctrl = new wxTextCtrl(parent, wxID_ANY, string_value); + + // Disable the textctrl, it's only there to show the absolute value from the slider + text_ctrl->Enable(false); + + // wxWidget takes over the pointer provided to it in the event handler. + // This won't be a memory leak, it'll be destroyed on dialog close. + slider->Bind(wxEVT_SLIDER, &PostProcessingConfigDiag::Event_Slider, + dialog, wxID_ANY, wxID_ANY, new UserEventData(m_option)); + + m_option_sliders.push_back(slider); + m_option_text_ctrls.push_back(text_ctrl); + } + + if (vector_size == 1) + { + szr_values->Add(m_option_sliders[0], wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + szr_values->Add(m_option_text_ctrls[0]); + + sizer->Add(szr_values); + } + else + { + wxFlexGridSizer* const szr_inside = new wxFlexGridSizer(2, 0, 0); + for (size_t i = 0; i < vector_size; ++i) + { + szr_inside->Add(m_option_sliders[i], wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + szr_inside->Add(m_option_text_ctrls[i]); + } + + szr_values->Add(szr_inside); + sizer->Add(szr_values); + } + } +} + +void PostProcessingConfigDiag::ConfigGrouping::EnableDependentChildren(bool enable) +{ + // Enable or disable all the children + for (auto& it : m_children) + { + if (it->m_type == ConfigGrouping::WidgetType::TYPE_TOGGLE) + { + it->m_option_checkbox->Enable(enable); + } + else + { + for (auto& slider : it->m_option_sliders) + slider->Enable(enable); + } + // Set this objects children as well + it->EnableDependentChildren(enable); + } +} + +void PostProcessingConfigDiag::Event_CheckBox(wxCommandEvent &ev) +{ + UserEventData* config_option = (UserEventData*)ev.GetEventUserData(); + ConfigGrouping* config = m_config_map[config_option->GetData()]; + + m_post_processor->SetOptionb(config->GetOption(), ev.IsChecked()); + + config->EnableDependentChildren(ev.IsChecked()); + + ev.Skip(); +} + +void PostProcessingConfigDiag::Event_Slider(wxCommandEvent &ev) +{ + UserEventData* config_option = (UserEventData*)ev.GetEventUserData(); + ConfigGrouping* config = m_config_map[config_option->GetData()]; + + const auto& option_data = m_post_processor->GetOption(config->GetOption()); + + size_t vector_size = 0; + if (option_data.m_type == PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER) + vector_size = option_data.m_integer_values.size(); + else + vector_size = option_data.m_float_values.size(); + + for (size_t i = 0; i < vector_size; ++i) + { + // Got to do this garbage again. + // Convert current step in to the real range value + int current_step = config->GetSliderValue(i); + std::string string_value; + if (option_data.m_type == PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER) + { + s32 value = option_data.m_integer_step_values[i] * current_step + option_data.m_integer_min_values[i]; + m_post_processor->SetOptioni(config->GetOption(), i, value); + string_value = std::to_string(value); + + } + else + { + float value = option_data.m_float_step_values[i] * current_step + option_data.m_float_min_values[i]; + m_post_processor->SetOptionf(config->GetOption(), i, value); + string_value = std::to_string(value); + } + // Update the text box to include the new value + config->SetSliderText(i, string_value); + } + ev.Skip(); +} + +void PostProcessingConfigDiag::Event_ClickClose(wxCommandEvent&) +{ + Close(); +} + +void PostProcessingConfigDiag::Event_Close(wxCloseEvent& ev) +{ + EndModal(wxID_OK); +} + diff --git a/Source/Core/DolphinWX/PostProcessingConfigDiag.h b/Source/Core/DolphinWX/PostProcessingConfigDiag.h new file mode 100644 index 0000000000..dabf44468b --- /dev/null +++ b/Source/Core/DolphinWX/PostProcessingConfigDiag.h @@ -0,0 +1,106 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "VideoCommon/PostProcessing.h" + +class PostProcessingConfigDiag : public wxDialog +{ +public: + PostProcessingConfigDiag(wxWindow* parent, const std::string& shader); + ~PostProcessingConfigDiag(); + +private: + + // This is literally the stupidest thing ever + // wxWidgets takes ownership of any pointer given to a event handler + // Instead of passing them a pointer to a std::string, we wrap around it here. + class UserEventData : public wxObject + { + public: + UserEventData(const std::string& data) : m_data(data) {} + const std::string& GetData() { return m_data; } + private: + const std::string m_data; + }; + + class ConfigGrouping + { + public: + enum WidgetType + { + TYPE_TOGGLE, + TYPE_SLIDER, + }; + + ConfigGrouping(WidgetType type, const std::string& gui_name, + const std::string& option_name, const std::string& parent, + const PostProcessingShaderConfiguration::ConfigurationOption* config_option) + : m_type(type), m_gui_name(gui_name), + m_option(option_name), m_parent(parent), + m_config_option(config_option) {} + + void AddChild(ConfigGrouping* child) { m_children.push_back(child); } + bool HasChildren() { return m_children.size() != 0; } + std::vector& GetChildren() { return m_children; } + + // Gets the string that is shown in the UI for the option + const std::string& GetGUIName() { return m_gui_name; } + // Gets the option name for use in the shader + // Also is a unique identifier for the option's configuration + const std::string& GetOption() { return m_option; } + // Gets the option name of the parent of this option + const std::string& GetParent() { return m_parent; } + + void GenerateUI(PostProcessingConfigDiag* dialog, wxWindow* parent, wxFlexGridSizer* sizer); + + void EnableDependentChildren(bool enable); + + int GetSliderValue(const int index) { return m_option_sliders[index]->GetValue(); } + void SetSliderText(const int index, const std::string& text) { m_option_text_ctrls[index]->SetValue(text); } + + private: + const WidgetType m_type; + const std::string m_gui_name; + const std::string m_option; + const std::string m_parent; + const PostProcessingShaderConfiguration::ConfigurationOption* m_config_option; + + // For TYPE_TOGGLE + wxCheckBox* m_option_checkbox; + + // For TYPE_SLIDER + // Can have up to 4 + std::vector m_option_sliders; + std::vector m_option_text_ctrls; + + std::vector m_children; + }; + + // WX UI things + void Event_Close(wxCloseEvent&); + void Event_ClickClose(wxCommandEvent&); + void Event_Slider(wxCommandEvent &ev); + void Event_CheckBox(wxCommandEvent &ev); + + const std::string& m_shader; + PostProcessingShaderConfiguration* m_post_processor; + + std::map m_config_map; + std::vector m_config_groups; +}; + diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index 13f3dad9b4..0bd5a8ccff 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -1,3 +1,7 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + #include #include #include @@ -32,6 +36,7 @@ #include "DolphinWX/VideoConfigDiag.h" #include "DolphinWX/WxUtils.h" #include "VideoBackends/OGL/main.h" +#include "VideoCommon/PostProcessing.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" @@ -401,10 +406,13 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con // postproc shader if (vconfig.backend_info.PPShaders.size()) { + wxFlexGridSizer* const szr_pp = new wxFlexGridSizer(3, 5, 5); wxChoice *const choice_ppshader = new wxChoice(page_enh, -1); RegisterControl(choice_ppshader, wxGetTranslation(ppshader_desc)); choice_ppshader->AppendString(_("(off)")); + button_config_pp = new wxButton(page_enh, wxID_ANY, _("Config")); + for (const std::string& shader : vconfig.backend_info.PPShaders) { choice_ppshader->AppendString(StrToWxStr(shader)); @@ -421,9 +429,12 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con button_config_pp->Enable(postprocessing_shader.HasOptions()); choice_ppshader->Bind(wxEVT_CHOICE, &VideoConfigDiag::Event_PPShader, this); + button_config_pp->Bind(wxEVT_BUTTON, &VideoConfigDiag::Event_ConfigurePPShader, this); szr_enh->Add(new wxStaticText(page_enh, -1, _("Post-Processing Effect:")), 1, wxALIGN_CENTER_VERTICAL, 0); - szr_enh->Add(choice_ppshader); + szr_pp->Add(choice_ppshader); + szr_pp->Add(button_config_pp); + szr_enh->Add(szr_pp); } // Scaled copy, PL, Bilinear filter diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h index 6802f0be84..261fa2f6ec 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.h +++ b/Source/Core/DolphinWX/VideoConfigDiag.h @@ -1,9 +1,14 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + #pragma once #include #include #include #include +#include #include #include #include @@ -22,7 +27,9 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/CoreParameter.h" +#include "DolphinWX/PostProcessingConfigDiag.h" #include "DolphinWX/WxUtils.h" +#include "VideoCommon/PostProcessing.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" @@ -201,6 +208,8 @@ protected: wxStaticText* text_aamode; SettingChoice* choice_aamode; + wxButton* button_config_pp; + SettingCheckBox* borderless_fullscreen; SettingRadioButton* efbcopy_texture;