diff --git a/apps/openmw/mwgui/postprocessorhud.cpp b/apps/openmw/mwgui/postprocessorhud.cpp index 777468e8b0..2fa4760867 100644 --- a/apps/openmw/mwgui/postprocessorhud.cpp +++ b/apps/openmw/mwgui/postprocessorhud.cpp @@ -30,7 +30,7 @@ namespace { auto technique = processor->getTechniques()[i]; - if (!technique) + if (!technique || technique->getDynamic()) continue; chain << technique->getName(); @@ -140,10 +140,15 @@ namespace MWGui { auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); mOverrideHint = list->getItemNameAt(selected); + + auto technique = *list->getItemDataAt>(selected); + if (technique->getDynamic()) + return; + if (enabled) - processor->enableTechnique(*list->getItemDataAt>(selected)); + processor->enableTechnique(technique); else - processor->disableTechnique(*list->getItemDataAt>(selected)); + processor->disableTechnique(technique); saveChain(); } } @@ -172,7 +177,11 @@ namespace MWGui if (static_cast(index) != selected) { - if (processor->enableTechnique(*mActiveList->getItemDataAt>(selected), index)) + auto technique = *mActiveList->getItemDataAt>(selected); + if (technique->getDynamic()) + return; + + if (processor->enableTechnique(technique, index) != MWRender::PostProcessor::Status_Error) saveChain(); } } @@ -330,6 +339,9 @@ namespace MWGui { case fx::Technique::Status::Success: case fx::Technique::Status::Uncompiled: + { + if (technique->getDynamic()) + ss << "#{fontcolourhtml=header}Locked: #{fontcolourhtml=normal} Cannot be toggled or moved, controlled by external Lua script" << endl << endl; ss << "#{fontcolourhtml=header}Author: #{fontcolourhtml=normal} " << author << endl << endl << "#{fontcolourhtml=header}Version: #{fontcolourhtml=normal} " << version << endl << endl << "#{fontcolourhtml=header}Description: #{fontcolourhtml=normal} " << description << endl << endl @@ -338,6 +350,7 @@ namespace MWGui << "#{fontcolourhtml=header} Underwater: #{fontcolourhtml=normal} " << flag_underwater << "#{fontcolourhtml=header} 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(); diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 6894208c07..3525906aac 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -16,6 +16,8 @@ #include "../mwbase/windowmanager.hpp" +#include "../mwrender/postprocessor.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/ptr.hpp" @@ -271,6 +273,7 @@ namespace MWLua LuaUi::clearUserInterface(); mUiResourceManager.clear(); MWBase::Environment::get().getWindowManager()->setConsoleMode(""); + MWBase::Environment::get().getWorld()->getPostProcessor()->disableDynamicShaders(); mActiveLocalScripts.clear(); mLocalEvents.clear(); mGlobalEvents.clear(); diff --git a/apps/openmw/mwlua/postprocessingbindings.cpp b/apps/openmw/mwlua/postprocessingbindings.cpp index 8699c5654a..8ae9cd10a8 100644 --- a/apps/openmw/mwlua/postprocessingbindings.cpp +++ b/apps/openmw/mwlua/postprocessingbindings.cpp @@ -117,10 +117,9 @@ namespace MWLua [=, &shader] { shader.mQueuedAction = Shader::Action_None; - if (!MWBase::Environment::get().getWorld()->getPostProcessor()->enableTechnique(shader.mShader, pos)) + if (MWBase::Environment::get().getWorld()->getPostProcessor()->enableTechnique(shader.mShader, pos) == MWRender::PostProcessor::Status_Error) throw std::runtime_error("Failed enabling shader '" + shader.mShader->getName() + "'"); - }, - "Enable shader " + (shader.mShader ? shader.mShader->getName() : "nil") + } ); }; @@ -132,10 +131,9 @@ namespace MWLua [&] { shader.mQueuedAction = Shader::Action_None; - if (!MWBase::Environment::get().getWorld()->getPostProcessor()->disableTechnique(shader.mShader)) + if (MWBase::Environment::get().getWorld()->getPostProcessor()->disableTechnique(shader.mShader) == MWRender::PostProcessor::Status_Error) throw std::runtime_error("Failed disabling shader '" + shader.mShader->getName() + "'"); - }, - "Disable shader " + (shader.mShader ? shader.mShader->getName() : "nil") + } ); }; @@ -168,6 +166,9 @@ namespace MWLua if (!shader.mShader || !shader.mShader->isValid()) throw std::runtime_error(Misc::StringUtils::format("Failed loading shader '%s'", name)); + if (!shader.mShader->getDynamic()) + throw std::runtime_error(Misc::StringUtils::format("Shader '%s' is not marked as dynamic", name)); + return shader; }; diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 61468df7da..38ee13831c 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -182,7 +182,6 @@ namespace MWRender if (!path.parent_path().has_parent_path() && fileExt == fx::Technique::sExt) { auto absolutePath = std::filesystem::path(mVFS->getAbsoluteFileName(name)); - mTechniqueFileMap[absolutePath.stem().string()] = absolutePath; } } @@ -210,7 +209,6 @@ namespace MWRender if (mUsePostProcessing && mTechniqueFileMap.empty()) { populateTechniqueFiles(); - } mMainTemplate->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); @@ -648,16 +646,16 @@ namespace MWRender mRendering.getSkyManager()->setSunglare(sunglare); } - bool PostProcessor::enableTechnique(std::shared_ptr technique, std::optional location) + PostProcessor::Status PostProcessor::enableTechnique(std::shared_ptr technique, std::optional location) { if (!isEnabled()) { Log(Debug::Warning) << "PostProcessing disabled, cannot load technique '" << technique->getName() << "'"; - return false; + return Status_Error; } if (!technique || Misc::StringUtils::ciEqual(technique->getName(), "main") || (location.has_value() && location.value() <= 0)) - return false; + return Status_Error; disableTechnique(technique, false); @@ -666,23 +664,23 @@ namespace MWRender mTechniques.insert(mTechniques.begin() + pos, technique); dirtyTechniques(); - return true; + return Status_Toggled; } - bool PostProcessor::disableTechnique(std::shared_ptr technique, bool dirty) + PostProcessor::Status PostProcessor::disableTechnique(std::shared_ptr technique, bool dirty) { if (Misc::StringUtils::ciEqual(technique->getName(), "main")) - return false; + return Status_Error; auto it = std::find(mTechniques.begin(), mTechniques.end(), technique); if (it == std::end(mTechniques)) - return false; + return Status_Unchanged; mTechniques.erase(it); if (dirty) dirtyTechniques(); - return true; + return Status_Toggled; } bool PostProcessor::isTechniqueEnabled(const std::shared_ptr& technique) const @@ -792,7 +790,7 @@ namespace MWRender if (technique->getStatus() != fx::Technique::Status::File_Not_exists) technique->setLastModificationTime(std::filesystem::last_write_time(mTechniqueFileMap[technique->getName()])); - if (!loadNextFrame) + if (loadNextFrame) { mQueuedTemplates.push_back(technique); return technique; @@ -853,5 +851,12 @@ namespace MWRender dirtyTechniques(); } + + void PostProcessor::disableDynamicShaders() + { + for (auto& technique : mTechniques) + if (technique->getDynamic()) + disableTechnique(technique); + } } diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index 6663710ced..1229d4da88 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -110,9 +110,16 @@ namespace MWRender void resize(); - bool enableTechnique(std::shared_ptr technique, std::optional location = std::nullopt); + enum Status + { + Status_Error, + Status_Toggled, + Status_Unchanged + }; - bool disableTechnique(std::shared_ptr technique, bool dirty = true); + Status enableTechnique(std::shared_ptr technique, std::optional location = std::nullopt); + + Status disableTechnique(std::shared_ptr technique, bool dirty = true); bool getSupportsNormalsRT() const { return mNormalsSupported; } @@ -154,7 +161,7 @@ namespace MWRender void toggleMode(); - std::shared_ptr loadTechnique(const std::string& name, bool loadNextFrame=true); + std::shared_ptr loadTechnique(const std::string& name, bool loadNextFrame=false); bool isEnabled() const { return mUsePostProcessing && mEnabled; } @@ -168,6 +175,8 @@ namespace MWRender void setRenderTargetSize(int width, int height) { mWidth = width; mHeight = height; } + void disableDynamicShaders(); + private: void populateTechniqueFiles(); diff --git a/components/fx/technique.cpp b/components/fx/technique.cpp index 2b8f37c96f..69999f1aa9 100644 --- a/components/fx/technique.cpp +++ b/components/fx/technique.cpp @@ -75,6 +75,7 @@ namespace fx mGLSLExtensions.clear(); mGLSLVersion = mUBO ? 330 : 120; mGLSLProfile.clear(); + mDynamic = false; } std::string Technique::getBlockWithLineDirective() @@ -249,11 +250,13 @@ namespace fx for (const auto& ext : parseLiteralList()) mGLSLExtensions.emplace(ext); } + else if (key == "dynamic") + mDynamic = parseBool(); else error(Misc::StringUtils::format("unexpected key '%s'", std::string{key})); expect(); - } + } if (mPassKeys.empty()) error("pass list in 'technique' block cannot be empty."); diff --git a/components/fx/technique.hpp b/components/fx/technique.hpp index 577c592ad0..057738e952 100644 --- a/components/fx/technique.hpp +++ b/components/fx/technique.hpp @@ -159,6 +159,8 @@ namespace fx UniformMap::iterator findUniform(const std::string& name); + bool getDynamic() const { return mDynamic; } + private: [[noreturn]] void error(const std::string& msg); @@ -275,6 +277,8 @@ namespace fx std::string mBuffer; std::string mLastError; + + bool mDynamic = false; }; template<> void Technique::parseBlockImp(); diff --git a/docs/source/reference/postprocessing/lua.rst b/docs/source/reference/postprocessing/lua.rst index cdc6dd93d0..45ccdd3631 100644 --- a/docs/source/reference/postprocessing/lua.rst +++ b/docs/source/reference/postprocessing/lua.rst @@ -5,8 +5,8 @@ Connecting With Lua Overview ######## -Every shader can be controlled through the Lua scripting system. While shaders can be disabled and enabled, -shader uniforms can also be controlled. For details reference the API documentation, found :doc:`here<../lua-scripting/openmw_postprocessing>`. +Every shader that is marked as ``dynamic`` can be controlled through the Lua scripting system. Shaders can be disabled and enabled, +and their uniforms can be controlled via scripts. For details, reference the API documentation :doc:`here<../lua-scripting/openmw_postprocessing>`. Toggling Shaders With a Keybind ############################### @@ -42,12 +42,14 @@ It is assumed the shader has the filename ``desaturate.omwfx`` in this example. technique { description = "Desaturates scene"; - passes = desaturate; version = "1.0"; author = "Fargoth"; passes = desaturate; + dynamic = true; } +.. note:: + The ``dynamic`` flag here is very important, otherwise Lua will not be able to interact with this shader. Next, a script that is attached to the player is needed. The shader is loaded first, then toggled on and off in response to key presses. Below is a working example to illustrate this. @@ -83,11 +85,11 @@ hidden in this HUD, this can done by adding the ``hidden`` flag to the main tech technique { description = "Desaturates scene"; - passes = desaturate; version = "1.0"; author = "Fargoth"; passes = desaturate; flags = hidden; + dynamic = true; } This flag is usually used when the shader is associated with something special, like special weather, spell, or alcohol effects. diff --git a/docs/source/reference/postprocessing/omwfx.rst b/docs/source/reference/postprocessing/omwfx.rst index 8e8cab2c5a..7b5c6d2e79 100644 --- a/docs/source/reference/postprocessing/omwfx.rst +++ b/docs/source/reference/postprocessing/omwfx.rst @@ -314,6 +314,11 @@ Exactly one ``technique`` block is required for every shader file. In this we de +------------------+--------------------+---------------------------------------------------+ | flags | `SHADER_FLAG`_ | ``,`` separated list of shader flags | +------------------+--------------------+---------------------------------------------------+ +| dynamic | boolean | Whether shader is exposed to Lua | ++------------------+--------------------+---------------------------------------------------+ + +When ``dynamic`` is set to ``true``, the shaders order cannot be manually moved, enabled, or disabled. The shaders state +can only be controlled via a Lua script. In the code snippet below, a shader is defined that requires GLSL `330`, HDR capatiblities, and is only enabled underwater in exteriors.