diff --git a/apps/openmw/mwgui/postprocessorhud.cpp b/apps/openmw/mwgui/postprocessorhud.cpp index 6f73c5a9fd..8f5b20ba98 100644 --- a/apps/openmw/mwgui/postprocessorhud.cpp +++ b/apps/openmw/mwgui/postprocessorhud.cpp @@ -168,10 +168,11 @@ namespace MWGui if (static_cast(index) != selected) { auto technique = *mActiveList->getItemDataAt>(selected); - if (technique->getDynamic()) + if (technique->getDynamic() || technique->getInternal()) return; - if (processor->enableTechnique(std::move(technique), index) != MWRender::PostProcessor::Status_Error) + if (processor->enableTechnique(std::move(technique), index - mOffset) + != MWRender::PostProcessor::Status_Error) processor->saveChain(); } } @@ -444,10 +445,16 @@ namespace MWGui } } + mOffset = 0; for (auto technique : processor->getTechniques()) { if (!technique->getHidden()) + { mActiveList->addItem(technique->getName(), technique); + + if (technique->getInternal()) + mOffset++; + } } auto tryFocus = [this](ListWrapper* widget, const std::string& hint) { @@ -484,6 +491,7 @@ namespace MWGui factory.registerFactory("Widget"); factory.registerFactory("Widget"); factory.registerFactory("Widget"); + factory.registerFactory("Widget"); factory.registerFactory("Widget"); } } diff --git a/apps/openmw/mwgui/postprocessorhud.hpp b/apps/openmw/mwgui/postprocessorhud.hpp index e08d20e358..20e27bac3a 100644 --- a/apps/openmw/mwgui/postprocessorhud.hpp +++ b/apps/openmw/mwgui/postprocessorhud.hpp @@ -96,6 +96,8 @@ namespace MWGui Gui::AutoSizedEditBox* mShaderInfo; std::string mOverrideHint; + + int mOffset = 0; }; } diff --git a/apps/openmw/mwrender/pingpongcanvas.cpp b/apps/openmw/mwrender/pingpongcanvas.cpp index 3af937045f..285dde884a 100644 --- a/apps/openmw/mwrender/pingpongcanvas.cpp +++ b/apps/openmw/mwrender/pingpongcanvas.cpp @@ -1,5 +1,7 @@ #include "pingpongcanvas.hpp" +#include + #include #include #include diff --git a/components/fx/technique.cpp b/components/fx/technique.cpp index f6bc881f78..73355de9a5 100644 --- a/components/fx/technique.cpp +++ b/components/fx/technique.cpp @@ -498,6 +498,37 @@ namespace fx mDefinedUniforms.emplace_back(std::move(uniform)); } + template + SrcT Technique::getUniformValue() + { + constexpr bool isVec + = std::is_same_v || std::is_same_v || std::is_same_v; + constexpr bool isFloat = std::is_same_v; + constexpr bool isInt = std::is_same_v; + constexpr bool isBool = std::is_same_v; + + static_assert(isVec || isFloat || isInt || isBool, "Unsupported type"); + + if constexpr (isVec) + { + return parseVec(); + } + else if constexpr (isFloat) + { + return parseFloat(); + } + else if constexpr (isInt) + { + return parseInteger(); + } + else if constexpr (isBool) + { + return parseBool(); + } + + error(Misc::StringUtils::format("failed setting uniform type")); + } + template void Technique::parseUniform() { @@ -515,28 +546,13 @@ namespace fx expect("error parsing config for uniform block"); - constexpr bool isVec = std::is_same_v || std::is_same_v || std::is_same_v; - constexpr bool isFloat = std::is_same_v; - constexpr bool isInt = std::is_same_v; - constexpr bool isBool = std::is_same_v; - - static_assert(isVec || isFloat || isInt || isBool, "Unsupported type"); - if (key == "default") { - if constexpr (isVec) - data.mDefault = parseVec(); - else if constexpr (isFloat) - data.mDefault = parseFloat(); - else if constexpr (isInt) - data.mDefault = parseInteger(); - else if constexpr (isBool) - data.mDefault = parseBool(); + data.mDefault = getUniformValue(); } else if (key == "size") { - if constexpr (isBool) + if constexpr (std::is_same_v) error("bool arrays currently unsupported"); int size = parseInteger(); @@ -545,25 +561,11 @@ namespace fx } else if (key == "min") { - if constexpr (isVec) - data.mMin = parseVec(); - else if constexpr (isFloat) - data.mMin = parseFloat(); - else if constexpr (isInt) - data.mMin = parseInteger(); - else if constexpr (isBool) - data.mMin = parseBool(); + data.mMin = getUniformValue(); } else if (key == "max") { - if constexpr (isVec) - data.mMax = parseVec(); - else if constexpr (isFloat) - data.mMax = parseFloat(); - else if constexpr (isInt) - data.mMax = parseInteger(); - else if constexpr (isBool) - data.mMax = parseBool(); + data.mMax = getUniformValue(); } else if (key == "step") uniform->mStep = parseFloat(); @@ -571,18 +573,19 @@ namespace fx uniform->mStatic = parseBool(); else if (key == "description") { - expect(); - uniform->mDescription = std::get(mToken).value; + uniform->mDescription = parseString(); } else if (key == "header") { - expect(); - uniform->mHeader = std::get(mToken).value; + uniform->mHeader = parseString(); } else if (key == "display_name") { - expect(); - uniform->mDisplayName = std::get(mToken).value; + uniform->mDisplayName = parseString(); + } + else if (key == "widget_type") + { + parseWidgetType(data); } else error(Misc::StringUtils::format("unexpected key '%s'", std::string{ key })); @@ -1001,6 +1004,60 @@ namespace fx error(Misc::StringUtils::format("unrecognized blend function '%s'", std::string{ asLiteral() })); } + template + void Technique::parseWidgetType(Types::Uniform& uniform) + { + expect(); + + if (asLiteral() == "choice") + { + /* Example usage + + widget_type = choice( + "Option A": , + "Option B": , + "Option C": + ); + + */ + expect(); + + std::vector> choices; + + while (!isNext()) + { + fx::Types::Choice choice; + choice.mLabel = parseString(); + expect(); + choice.mValue = getUniformValue(); + choices.push_back(choice); + + if (isNext()) + { + mToken = mLexer->next(); + + // Handle leading comma + if (isNext()) + { + break; + } + + continue; + } + + break; + } + + uniform.mChoices = std::move(choices); + + expect(); + } + else + { + error(Misc::StringUtils::format("unrecognized widget type '%s'", std::string{ asLiteral() })); + } + } + bool Technique::parseBool() { mToken = mLexer->next(); diff --git a/components/fx/technique.hpp b/components/fx/technique.hpp index 01943a2fbe..2778763a9a 100644 --- a/components/fx/technique.hpp +++ b/components/fx/technique.hpp @@ -228,6 +228,12 @@ namespace fx int parseSourceFormat(); + template + void parseWidgetType(Types::Uniform& uniform); + + template + SrcT getUniformValue(); + osg::BlendEquation::Equation parseBlendEquation(); osg::BlendFunc::BlendFuncMode parseBlendFuncMode(); diff --git a/components/fx/types.hpp b/components/fx/types.hpp index 829bf176b7..596b54c217 100644 --- a/components/fx/types.hpp +++ b/components/fx/types.hpp @@ -4,15 +4,9 @@ #include #include -#include -#include -#include -#include #include #include -#include - #include #include #include @@ -56,6 +50,13 @@ namespace fx osg::Vec4f mClearColor = osg::Vec4f(0.0, 0.0, 0.0, 1.0); }; + template + struct Choice + { + std::string mLabel; + T mValue; + }; + template struct Uniform { @@ -66,6 +67,8 @@ namespace fx T mMin = std::numeric_limits::lowest(); T mMax = std::numeric_limits::max(); + std::vector> mChoices; + using value_type = T; bool isArray() const { return mArray.has_value(); } diff --git a/components/fx/widgets.cpp b/components/fx/widgets.cpp index f8d12fdba3..8382ca2d56 100644 --- a/components/fx/widgets.cpp +++ b/components/fx/widgets.cpp @@ -28,35 +28,20 @@ namespace fx { void EditBool::setValue(bool value) { - auto uniform = mUniform.lock(); - - if (!uniform) - return; - mCheckbutton->setCaptionWithReplacing(value ? "#{Interface:On}" : "#{Interface:Off}"); mFill->setVisible(value); - uniform->setValue(value); + mUniform->setValue(value); } void EditBool::setValueFromUniform() { - auto uniform = mUniform.lock(); - - if (!uniform) - return; - - setValue(uniform->template getValue()); + setValue(mUniform->template getValue()); } void EditBool::toDefault() { - auto uniform = mUniform.lock(); - - if (!uniform) - return; - - setValue(uniform->getDefault()); + setValue(mUniform->getDefault()); } void EditBool::initialiseOverride() @@ -71,12 +56,75 @@ namespace fx void EditBool::notifyMouseButtonClick(MyGUI::Widget* sender) { - auto uniform = mUniform.lock(); + setValue(!mUniform->getValue()); + } + + template + void EditChoice::setValue(const T& value) + { + // Update the combo view + for (size_t i = 0; i < this->mChoices->getItemCount(); i++) + { + if (*this->mChoices->getItemDataAt(i) == value) + { + this->mChoices->setIndexSelected(i); + break; + } + } + + mUniform->template setValue(value); + } + + void EditChoice::notifyComboBoxChanged(MyGUI::ComboBox* sender, size_t pos) + { + std::visit( + [this, sender, pos](auto&& data) { + using T = typename std::decay_t::value_type; + setValue(*sender->getItemDataAt(pos)); + }, + mUniform->mData); + } + + void EditChoice::setValueFromUniform() + { + std::visit( + [this](auto&& data) { + using T = typename std::decay_t::value_type; + size_t index = 0; + for (const auto& choice : data.mChoices) + { + this->mChoices->addItem(choice.mLabel, choice.mValue); + + if (choice.mValue == mUniform->template getValue()) + { + this->mChoices->setIndexSelected(index); + } + + index++; + } + + setValue(mUniform->template getValue()); + }, + mUniform->mData); + } + + void EditChoice::toDefault() + { + std::visit( + [this](auto&& data) { + using T = typename std::decay_t::value_type; + setValue(mUniform->template getDefault()); + }, + mUniform->mData); + } + + void EditChoice::initialiseOverride() + { + Base::initialiseOverride(); - if (!uniform) - return; + assignWidget(mChoices, "Choices"); - setValue(!uniform->getValue()); + mChoices->eventComboChangePosition += MyGUI::newDelegate(this, &EditChoice::notifyComboBoxChanged); } void UniformBase::init(const std::shared_ptr& uniform) @@ -101,38 +149,48 @@ namespace fx [this, &uniform](auto&& arg) { using T = typename std::decay_t::value_type; - if constexpr (std::is_same_v) - { - createVectorWidget(uniform, mClient, this); - } - else if constexpr (std::is_same_v) + if (arg.mChoices.size() > 0) { - createVectorWidget(uniform, mClient, this); - } - else if constexpr (std::is_same_v) - { - createVectorWidget(uniform, mClient, this); - } - else if constexpr (std::is_same_v) - { - auto* widget = mClient->createWidget("MW_ValueEditNumber", - { 0, 0, mClient->getWidth(), mClient->getHeight() }, MyGUI::Align::Stretch); - widget->setData(uniform); - mBases.emplace_back(widget); - } - else if constexpr (std::is_same_v) - { - auto* widget = mClient->createWidget("MW_ValueEditNumber", + auto* widget = mClient->createWidget("MW_ValueEditChoice", { 0, 0, mClient->getWidth(), mClient->getHeight() }, MyGUI::Align::Stretch); widget->setData(uniform); mBases.emplace_back(widget); } - else if constexpr (std::is_same_v) + else { - auto* widget = mClient->createWidget("MW_ValueEditBool", - { 0, 0, mClient->getWidth(), mClient->getHeight() }, MyGUI::Align::Stretch); - widget->setData(uniform); - mBases.emplace_back(widget); + if constexpr (std::is_same_v) + { + createVectorWidget(uniform, mClient, this); + } + else if constexpr (std::is_same_v) + { + createVectorWidget(uniform, mClient, this); + } + else if constexpr (std::is_same_v) + { + createVectorWidget(uniform, mClient, this); + } + else if constexpr (std::is_same_v) + { + auto* widget = mClient->createWidget("MW_ValueEditNumber", + { 0, 0, mClient->getWidth(), mClient->getHeight() }, MyGUI::Align::Stretch); + widget->setData(uniform); + mBases.emplace_back(widget); + } + else if constexpr (std::is_same_v) + { + auto* widget = mClient->createWidget("MW_ValueEditNumber", + { 0, 0, mClient->getWidth(), mClient->getHeight() }, MyGUI::Align::Stretch); + widget->setData(uniform); + mBases.emplace_back(widget); + } + else if constexpr (std::is_same_v) + { + auto* widget = mClient->createWidget("MW_ValueEditBool", + { 0, 0, mClient->getWidth(), mClient->getHeight() }, MyGUI::Align::Stretch); + widget->setData(uniform); + mBases.emplace_back(widget); + } } mReset->eventMouseButtonClick += MyGUI::newDelegate(this, &UniformBase::notifyResetClicked); diff --git a/components/fx/widgets.hpp b/components/fx/widgets.hpp index baee2d6df4..6217af7fee 100644 --- a/components/fx/widgets.hpp +++ b/components/fx/widgets.hpp @@ -57,7 +57,7 @@ namespace fx virtual void toDefault() = 0; protected: - std::weak_ptr mUniform; + std::shared_ptr mUniform; Index mIndex; }; @@ -95,22 +95,19 @@ namespace fx float range = 0.f; float min = 0.f; - if (auto uniform = mUniform.lock()) + if constexpr (std::is_fundamental_v) { - if constexpr (std::is_fundamental_v) - { - uniform->template setValue(mValue); - range = uniform->template getMax() - uniform->template getMin(); - min = uniform->template getMin(); - } - else - { - UType uvalue = uniform->template getValue(); - uvalue[mIndex] = mValue; - uniform->template setValue(uvalue); - range = uniform->template getMax()[mIndex] - uniform->template getMin()[mIndex]; - min = uniform->template getMin()[mIndex]; - } + mUniform->template setValue(mValue); + range = mUniform->template getMax() - mUniform->template getMin(); + min = mUniform->template getMin(); + } + else + { + UType uvalue = mUniform->template getValue(); + uvalue[mIndex] = mValue; + mUniform->template setValue(uvalue); + range = mUniform->template getMax()[mIndex] - mUniform->template getMin()[mIndex]; + min = mUniform->template getMin()[mIndex]; } float fill = (range == 0.f) ? 1.f : (mValue - min) / range; @@ -119,28 +116,22 @@ namespace fx void setValueFromUniform() override { - if (auto uniform = mUniform.lock()) - { - T value; + T value; - if constexpr (std::is_fundamental_v) - value = uniform->template getValue(); - else - value = uniform->template getValue()[mIndex]; + if constexpr (std::is_fundamental_v) + value = mUniform->template getValue(); + else + value = mUniform->template getValue()[mIndex]; - setValue(value); - } + setValue(value); } void toDefault() override { - if (auto uniform = mUniform.lock()) - { - if constexpr (std::is_fundamental_v) - setValue(uniform->template getDefault()); - else - setValue(uniform->template getDefault()[mIndex]); - } + if constexpr (std::is_fundamental_v) + setValue(mUniform->template getDefault()); + else + setValue(mUniform->template getDefault()[mIndex]); } private: @@ -164,15 +155,10 @@ namespace fx void notifyMouseWheel(MyGUI::Widget* sender, int rel) { - auto uniform = mUniform.lock(); - - if (!uniform) - return; - if (rel > 0) - increment(uniform->mStep); + increment(mUniform->mStep); else - increment(-uniform->mStep); + increment(-mUniform->mStep); } void notifyMouseButtonDragged(MyGUI::Widget* sender, int left, int top, MyGUI::MouseButton id) @@ -180,24 +166,19 @@ namespace fx if (id != MyGUI::MouseButton::Left) return; - auto uniform = mUniform.lock(); - - if (!uniform) - return; - int delta = left - mLastPointerX; // allow finer tuning when shift is pressed constexpr double scaling = 20.0; T step - = MyGUI::InputManager::getInstance().isShiftPressed() ? uniform->mStep / scaling : uniform->mStep; + = MyGUI::InputManager::getInstance().isShiftPressed() ? mUniform->mStep / scaling : mUniform->mStep; if (step == 0) { if constexpr (std::is_integral_v) step = 1; else - step = uniform->mStep; + step = mUniform->mStep; } if (delta > 0) @@ -218,30 +199,20 @@ namespace fx void increment(T step) { - auto uniform = mUniform.lock(); - - if (!uniform) - return; - if constexpr (std::is_fundamental_v) - setValue(std::clamp(uniform->template getValue() + step, - uniform->template getMin(), uniform->template getMax())); + setValue(std::clamp(mUniform->template getValue() + step, + mUniform->template getMin(), mUniform->template getMax())); else - setValue(std::clamp(uniform->template getValue()[mIndex] + step, - uniform->template getMin()[mIndex], uniform->template getMax()[mIndex])); + setValue(std::clamp(mUniform->template getValue()[mIndex] + step, + mUniform->template getMin()[mIndex], mUniform->template getMax()[mIndex])); } void notifyButtonClicked(MyGUI::Widget* sender) { - auto uniform = mUniform.lock(); - - if (!uniform) - return; - if (sender == mButtonDecrease) - increment(-uniform->mStep); + increment(-mUniform->mStep); else if (sender == mButtonIncrease) - increment(uniform->mStep); + increment(mUniform->mStep); } MyGUI::Button* mButtonDecrease{ nullptr }; @@ -275,6 +246,23 @@ namespace fx MYGUI_RTTI_DERIVED(EditNumberInt) }; + class EditChoice : public EditBase, public MyGUI::Widget + { + MYGUI_RTTI_DERIVED(EditChoice) + + public: + template + void setValue(const T& value); + void setValueFromUniform() override; + void toDefault() override; + + private: + void initialiseOverride() override; + void notifyComboBoxChanged(MyGUI::ComboBox* sender, size_t pos); + + MyGUI::ComboBox* mChoices{ nullptr }; + }; + class UniformBase final : public MyGUI::Widget { MYGUI_RTTI_DERIVED(UniformBase) diff --git a/docs/source/reference/postprocessing/omwfx.rst b/docs/source/reference/postprocessing/omwfx.rst index b47e509925..f9335e5c88 100644 --- a/docs/source/reference/postprocessing/omwfx.rst +++ b/docs/source/reference/postprocessing/omwfx.rst @@ -451,21 +451,21 @@ To use the sampler, define the appropriately named `sampler2D` in any of your pa It is possible to define settings for your shaders that can be adjusted by either users or a Lua script. -+-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+ -| Block | default | min | max | static | step | description | display_name | header | -+=================+==========+==========+==========+=========+==========+==============+===================+=========+ -|``uniform_bool`` | boolean | x | x | boolean | x | string | string | string | -+-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+ -|``uniform_float``| float | float | float | boolean | float | string | string | string | -+-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+ -|``uniform_int`` | integer | integer | integer | boolean | integer | string | string | string | -+-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+ -|``uniform_vec2`` | vec2 | vec2 | vec2 | boolean | vec2 | string | string | string | -+-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+ -|``uniform_vec3`` | vec3 | vec3 | vec3 | boolean | vec3 | string | string | string | -+-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+ -|``uniform_vec4`` | vec4 | vec4 | vec4 | boolean | vec4 | string | string | string | -+-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+ ++-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+--------------+ +| Block | default | min | max | static | step | description | display_name | header | widget_type | ++=================+==========+==========+==========+=========+==========+==============+===================+=========+==============+ +|``uniform_bool`` | boolean | x | x | boolean | x | string | string | string | choice(...) | ++-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+--------------+ +|``uniform_float``| float | float | float | boolean | float | string | string | string | choice(...) | ++-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+--------------+ +|``uniform_int`` | integer | integer | integer | boolean | integer | string | string | string | choice(...) | ++-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+--------------+ +|``uniform_vec2`` | vec2 | vec2 | vec2 | boolean | vec2 | string | string | string | choice(...) | ++-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+--------------+ +|``uniform_vec3`` | vec3 | vec3 | vec3 | boolean | vec3 | string | string | string | choice(...) | ++-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+--------------+ +|``uniform_vec4`` | vec4 | vec4 | vec4 | boolean | vec4 | string | string | string | choice(...) | ++-----------------+----------+----------+----------+---------+----------+--------------+-------------------+---------+--------------+ The ``description`` field is used to display a toolip when viewed in the in-game HUD. The ``header`` field field can be used to organize uniforms into groups in the HUD. The ``display_name`` field can be used to create a @@ -509,6 +509,21 @@ These uniform blocks must be defined with the new ``size`` parameter. size = 10; } +You may also define a dropdown list for users to select specific values from instead of the default sliders using the ``widget_type`` field. +Each item in the dropdown has an associated display name, which can be a localized string. + +.. code-block:: none + + uniform_int uStrength { + default = 2; + display_name = "Strength"; + widget_type = choice( + "Low" = 1, + "Medium" = 2, + "High" = 3 + ); + } + ``render_target`` ***************** diff --git a/files/data/mygui/openmw_postprocessor_hud.skin.xml b/files/data/mygui/openmw_postprocessor_hud.skin.xml index 8da8df2050..f6c6522a41 100644 --- a/files/data/mygui/openmw_postprocessor_hud.skin.xml +++ b/files/data/mygui/openmw_postprocessor_hud.skin.xml @@ -1,6 +1,16 @@ + + + + + + + + + +