1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 18:35:20 +00:00
OpenMW/apps/openmw/mwgui/postprocessorhud.cpp

445 lines
18 KiB
C++

#include "postprocessorhud.hpp"
#include <MyGUI_Window.h>
#include <MyGUI_Button.h>
#include <MyGUI_TabItem.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_FactoryManager.h>
#include <components/fx/widgets.hpp>
#include <components/fx/technique.hpp>
#include <components/widgets/box.hpp>
#include "../mwrender/postprocessor.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
namespace MWGui
{
void PostProcessorHud::ListWrapper::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch)
{
if (MyGUI::InputManager::getInstance().isShiftPressed() && (key == MyGUI::KeyCode::ArrowUp || key == MyGUI::KeyCode::ArrowDown))
return;
MyGUI::ListBox::onKeyButtonPressed(key, ch);
}
PostProcessorHud::PostProcessorHud()
: WindowBase("openmw_postprocessor_hud.layout")
{
getWidget(mTabConfiguration, "TabConfiguration");
getWidget(mActiveList, "ActiveList");
getWidget(mInactiveList, "InactiveList");
getWidget(mConfigLayout, "ConfigLayout");
getWidget(mFilter, "Filter");
getWidget(mButtonActivate, "ButtonActivate");
getWidget(mButtonDeactivate, "ButtonDeactivate");
getWidget(mButtonUp, "ButtonUp");
getWidget(mButtonDown, "ButtonDown");
mButtonActivate->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyActivatePressed);
mButtonDeactivate->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyDeactivatePressed);
mButtonUp->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyShaderUpPressed);
mButtonDown->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyShaderDownPressed);
mActiveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &PostProcessorHud::notifyKeyButtonPressed);
mInactiveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &PostProcessorHud::notifyKeyButtonPressed);
mActiveList->eventListChangePosition += MyGUI::newDelegate(this, &PostProcessorHud::notifyListChangePosition);
mInactiveList->eventListChangePosition += MyGUI::newDelegate(this, &PostProcessorHud::notifyListChangePosition);
mFilter->eventEditTextChange += MyGUI::newDelegate(this, &PostProcessorHud::notifyFilterChanged);
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &PostProcessorHud::notifyWindowResize);
mShaderInfo = mConfigLayout->createWidget<Gui::AutoSizedEditBox>("HeaderText", {}, MyGUI::Align::Default);
mShaderInfo->setUserString("VStretch", "true");
mShaderInfo->setUserString("HStretch", "true");
mShaderInfo->setTextAlign(MyGUI::Align::Left | MyGUI::Align::Top);
mShaderInfo->setEditReadOnly(true);
mShaderInfo->setEditWordWrap(true);
mShaderInfo->setEditMultiLine(true);
mShaderInfo->setNeedMouseFocus(false);
mConfigLayout->setVisibleVScroll(true);
mConfigArea = mConfigLayout->createWidget<MyGUI::Widget>("", {}, MyGUI::Align::Default);
mConfigLayout->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
mConfigArea->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
}
void PostProcessorHud::notifyFilterChanged(MyGUI::EditBox* sender)
{
updateTechniques();
}
void PostProcessorHud::notifyWindowResize(MyGUI::Window* sender)
{
layout();
}
void PostProcessorHud::notifyResetButtonClicked(MyGUI::Widget* sender)
{
for (size_t i = 1; i < mConfigArea->getChildCount(); ++i)
{
if (auto* child = dynamic_cast<fx::Widgets::UniformBase*>(mConfigArea->getChildAt(i)))
child->toDefault();
}
}
void PostProcessorHud::notifyListChangePosition(MyGUI::ListBox* sender, size_t index)
{
if (sender == mActiveList)
mInactiveList->clearIndexSelected();
else if (sender == mInactiveList)
mActiveList->clearIndexSelected();
if (index >= sender->getItemCount())
return;
updateConfigView(sender->getItemNameAt(index));
}
void PostProcessorHud::toggleTechnique(bool enabled)
{
auto* list = enabled ? mInactiveList : mActiveList;
size_t selected = list->getIndexSelected();
if (selected != MyGUI::ITEM_NONE)
{
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
mOverrideHint = list->getItemNameAt(selected);
auto technique = *list->getItemDataAt<std::shared_ptr<fx::Technique>>(selected);
if (technique->getDynamic())
return;
if (enabled)
processor->enableTechnique(technique);
else
processor->disableTechnique(technique);
processor->saveChain();
}
}
void PostProcessorHud::notifyActivatePressed(MyGUI::Widget* sender)
{
toggleTechnique(true);
}
void PostProcessorHud::notifyDeactivatePressed(MyGUI::Widget* sender)
{
toggleTechnique(false);
}
void PostProcessorHud::moveShader(Direction direction)
{
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
size_t selected = mActiveList->getIndexSelected();
if (selected == MyGUI::ITEM_NONE)
return;
int index = direction == Direction::Up ? static_cast<int>(selected) - 1 : selected + 1;
index = std::clamp<int>(index, 0, mActiveList->getItemCount() - 1);
if (static_cast<size_t>(index) != selected)
{
auto technique = *mActiveList->getItemDataAt<std::shared_ptr<fx::Technique>>(selected);
if (technique->getDynamic())
return;
if (processor->enableTechnique(technique, index) != MWRender::PostProcessor::Status_Error)
processor->saveChain();
}
}
void PostProcessorHud::notifyShaderUpPressed(MyGUI::Widget* sender)
{
moveShader(Direction::Up);
}
void PostProcessorHud::notifyShaderDownPressed(MyGUI::Widget* sender)
{
moveShader(Direction::Down);
}
void PostProcessorHud::notifyKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char ch)
{
MyGUI::ListBox* list = static_cast<MyGUI::ListBox*>(sender);
if (list->getIndexSelected() == MyGUI::ITEM_NONE)
return;
if (key == MyGUI::KeyCode::ArrowLeft && list == mActiveList)
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
{
toggleTechnique(false);
}
else
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mInactiveList);
mActiveList->clearIndexSelected();
select(mInactiveList, 0);
}
}
else if (key == MyGUI::KeyCode::ArrowRight && list == mInactiveList)
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
{
toggleTechnique(true);
}
else
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mActiveList);
mInactiveList->clearIndexSelected();
select(mActiveList, 0);
}
}
else if (list == mActiveList && MyGUI::InputManager::getInstance().isShiftPressed() && (key == MyGUI::KeyCode::ArrowUp || key == MyGUI::KeyCode::ArrowDown))
{
moveShader(key == MyGUI::KeyCode::ArrowUp ? Direction::Up : Direction::Down);
}
}
void PostProcessorHud::onOpen()
{
toggleMode(Settings::ShaderManager::Mode::Debug);
updateTechniques();
}
void PostProcessorHud::onClose()
{
toggleMode(Settings::ShaderManager::Mode::Normal);
}
void PostProcessorHud::layout()
{
constexpr int padding = 12;
constexpr int padding2 = padding * 2;
mShaderInfo->setCoord(padding, padding, mConfigLayout->getSize().width - padding2 - padding, mShaderInfo->getTextSize().height);
int totalHeight = mShaderInfo->getTop() + mShaderInfo->getTextSize().height + padding;
mConfigArea->setCoord({padding, totalHeight, mShaderInfo->getSize().width, mConfigLayout->getHeight()});
int childHeights = 0;
MyGUI::EnumeratorWidgetPtr enumerator = mConfigArea->getEnumerator();
while (enumerator.next())
{
enumerator.current()->setCoord(padding, childHeights + padding, mShaderInfo->getSize().width - padding2, enumerator.current()->getHeight());
childHeights += enumerator.current()->getHeight() + padding;
}
totalHeight += childHeights;
mConfigArea->setSize(mConfigArea->getWidth(), childHeights);
mConfigLayout->setCanvasSize(mConfigLayout->getWidth() - padding2, totalHeight);
mConfigLayout->setSize(mConfigLayout->getWidth(), mConfigLayout->getParentSize().height - padding2);
}
void PostProcessorHud::notifyMouseWheel(MyGUI::Widget *sender, int rel)
{
int offset = mConfigLayout->getViewOffset().top + rel * 0.3;
if (offset > 0)
mConfigLayout->setViewOffset(MyGUI::IntPoint(0, 0));
else
mConfigLayout->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(offset)));
}
void PostProcessorHud::select(ListWrapper* list, size_t index)
{
list->setIndexSelected(index);
notifyListChangePosition(list, index);
}
void PostProcessorHud::toggleMode(Settings::ShaderManager::Mode mode)
{
Settings::ShaderManager::get().setMode(mode);
MWBase::Environment::get().getWorld()->getPostProcessor()->toggleMode();
if (!isVisible())
return;
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
updateConfigView(mInactiveList->getItemNameAt(mInactiveList->getIndexSelected()));
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
updateConfigView(mActiveList->getItemNameAt(mActiveList->getIndexSelected()));
}
void PostProcessorHud::updateConfigView(const std::string& name)
{
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
auto technique = processor->loadTechnique(name);
if (!technique || technique->getStatus() == fx::Technique::Status::File_Not_exists)
return;
while (mConfigArea->getChildCount() > 0)
MyGUI::Gui::getInstance().destroyWidget(mConfigArea->getChildAt(0));
mShaderInfo->setCaption("");
std::ostringstream ss;
const std::string NA = "#{Interface:NotAvailableShort}";
const std::string endl = "\n";
std::string author = technique->getAuthor().empty() ? NA : std::string(technique->getAuthor());
std::string version = technique->getVersion().empty() ? NA : std::string(technique->getVersion());
std::string description = technique->getDescription().empty() ? NA : std::string(technique->getDescription());
auto serializeBool = [](bool value) {
return value ? "#{sYes}" : "#{sNo}";
};
const auto flags = technique->getFlags();
const auto flag_interior = serializeBool(!(flags & fx::Technique::Flag_Disable_Interiors));
const auto flag_exterior = serializeBool(!(flags & fx::Technique::Flag_Disable_Exteriors));
const auto flag_underwater = serializeBool(!(flags & fx::Technique::Flag_Disable_Underwater));
const auto flag_abovewater = serializeBool(!(flags & fx::Technique::Flag_Disable_Abovewater));
switch (technique->getStatus())
{
case fx::Technique::Status::Success:
case fx::Technique::Status::Uncompiled:
{
if (technique->getDynamic())
ss << "#{fontcolourhtml=header}#{PostProcessing:ShaderLocked}: #{fontcolourhtml=normal} #{PostProcessing:ShaderLockedDescription}" << endl << endl;
ss << "#{fontcolourhtml=header}#{PostProcessing:Author}: #{fontcolourhtml=normal} " << author << endl << endl
<< "#{fontcolourhtml=header}#{PostProcessing:Version}: #{fontcolourhtml=normal} " << version << endl << endl
<< "#{fontcolourhtml=header}#{PostProcessing:Description}: #{fontcolourhtml=normal} " << description << endl << endl
<< "#{fontcolourhtml=header}#{PostProcessing:InInteriors}: #{fontcolourhtml=normal} " << flag_interior
<< "#{fontcolourhtml=header} #{PostProcessing:InExteriors}: #{fontcolourhtml=normal} " << flag_exterior
<< "#{fontcolourhtml=header} #{PostProcessing:Underwater}: #{fontcolourhtml=normal} " << flag_underwater
<< "#{fontcolourhtml=header} #{PostProcessing:Abovewater}: #{fontcolourhtml=normal} " << flag_abovewater;
break;
}
case fx::Technique::Status::Parse_Error:
ss << "#{fontcolourhtml=negative}Shader Compile Error: #{fontcolourhtml=normal} <" << std::string(technique->getName()) << "> failed to compile." << endl << endl
<< technique->getLastError();
break;
case fx::Technique::Status::File_Not_exists:
break;
}
mShaderInfo->setCaptionWithReplacing(ss.str());
if (Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug)
{
if (technique->getUniformMap().size() > 0)
{
MyGUI::Button* resetButton = mConfigArea->createWidget<MyGUI::Button>("MW_Button", {0,0,0,24}, MyGUI::Align::Default);
resetButton->setCaptionWithReplacing("#{PostProcessing:ResetShader}");
resetButton->setTextAlign(MyGUI::Align::Center);
resetButton->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
resetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyResetButtonClicked);
}
for (const auto& uniform : technique->getUniformMap())
{
if (!uniform->mStatic || uniform->mSamplerType)
continue;
if (!uniform->mHeader.empty())
{
Gui::AutoSizedTextBox* divider = mConfigArea->createWidget<Gui::AutoSizedTextBox>("MW_UniformGroup", {0,0,0,34}, MyGUI::Align::Default);
divider->setNeedMouseFocus(false);
divider->setCaptionWithReplacing(uniform->mHeader);
}
fx::Widgets::UniformBase* uwidget = mConfigArea->createWidget<fx::Widgets::UniformBase>("MW_UniformEdit", {0,0,0,22}, MyGUI::Align::Default);
uwidget->init(uniform);
uwidget->getLabel()->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
}
}
layout();
}
void PostProcessorHud::updateTechniques()
{
if (!isVisible())
return;
std::string hint;
ListWrapper* hintWidget = nullptr;
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
{
hint = mInactiveList->getItemNameAt(mInactiveList->getIndexSelected());
hintWidget = mInactiveList;
}
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
{
hint = mActiveList->getItemNameAt(mActiveList->getIndexSelected());
hintWidget = mActiveList;
}
mInactiveList->removeAllItems();
mActiveList->removeAllItems();
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
for (const auto& [name, _] : processor->getTechniqueMap())
{
auto technique = processor->loadTechnique(name);
if (!technique)
continue;
if (!technique->getHidden() && !processor->isTechniqueEnabled(technique) && name.find(mFilter->getCaption()) != std::string::npos)
mInactiveList->addItem(name, technique);
}
for (auto technique : processor->getTechniques())
{
if (!technique->getHidden())
mActiveList->addItem(technique->getName(), technique);
}
auto tryFocus = [this](ListWrapper* widget, const std::string& hint)
{
size_t index = widget->findItemIndexWith(hint);
if (index != MyGUI::ITEM_NONE)
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(widget);
select(widget, index);
}
};
if (!mOverrideHint.empty())
{
tryFocus(mActiveList, mOverrideHint);
tryFocus(mInactiveList, mOverrideHint);
mOverrideHint.clear();
}
else if (hintWidget && !hint.empty())
tryFocus(hintWidget, hint);
}
void PostProcessorHud::registerMyGUIComponents()
{
MyGUI::FactoryManager& factory = MyGUI::FactoryManager::getInstance();
factory.registerFactory<fx::Widgets::UniformBase>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat4>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat3>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat2>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat>("Widget");
factory.registerFactory<fx::Widgets::EditNumberInt>("Widget");
factory.registerFactory<fx::Widgets::EditBool>("Widget");
factory.registerFactory<ListWrapper>("Widget");
}
}