From 6093cb5f2cc9b7ab89f8cc59a54165b91cb67f3b Mon Sep 17 00:00:00 2001 From: cody glassman Date: Sun, 22 May 2022 18:53:38 -0700 Subject: [PATCH] postprocessing lua api extensions --- apps/openmw/mwgui/postprocessorhud.cpp | 15 +-- apps/openmw/mwlua/postprocessingbindings.cpp | 105 ++++++++++++------ apps/openmw/mwrender/postprocessor.cpp | 51 +++++---- apps/openmw/mwrender/postprocessor.hpp | 15 ++- apps/openmw_test_suite/fx/technique.cpp | 2 +- components/fx/technique.cpp | 38 +++++-- components/fx/types.hpp | 73 +++++++++--- components/settings/shadermanager.hpp | 1 + .../source/reference/postprocessing/omwfx.rst | 12 +- .../reference/postprocessing/overview.rst | 2 +- files/lua_api/openmw/postprocessing.lua | 54 +++++++-- 11 files changed, 264 insertions(+), 104 deletions(-) diff --git a/apps/openmw/mwgui/postprocessorhud.cpp b/apps/openmw/mwgui/postprocessorhud.cpp index cfdbfda7e8..5e46f1e735 100644 --- a/apps/openmw/mwgui/postprocessorhud.cpp +++ b/apps/openmw/mwgui/postprocessorhud.cpp @@ -296,7 +296,7 @@ namespace MWGui auto technique = processor->loadTechnique(name); - if (!technique) + if (!technique || technique->getStatus() == fx::Technique::Status::File_Not_exists) return; while (mConfigArea->getChildCount() > 0) @@ -304,9 +304,6 @@ namespace MWGui mShaderInfo->setCaption(""); - if (!technique) - return; - std::ostringstream ss; const std::string NA = "NA"; @@ -322,8 +319,8 @@ namespace MWGui 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_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)); @@ -339,14 +336,12 @@ namespace MWGui << "#{fontcolourhtml=header} Underwater: #{fontcolourhtml=normal} " << flag_underwater << "#{fontcolourhtml=header} Abovewater: #{fontcolourhtml=normal} " << flag_abovewater; break; - case fx::Technique::Status::File_Not_exists: - ss << "#{fontcolourhtml=negative}Shader Error: #{fontcolourhtml=header} <" << std::string(technique->getFileName()) << ">#{fontcolourhtml=normal} not found." << endl << endl - << "Ensure the shader file is in a 'Shaders/' sub directory in a data files directory"; - 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()); diff --git a/apps/openmw/mwlua/postprocessingbindings.cpp b/apps/openmw/mwlua/postprocessingbindings.cpp index 187c9c700b..8699c5654a 100644 --- a/apps/openmw/mwlua/postprocessingbindings.cpp +++ b/apps/openmw/mwlua/postprocessingbindings.cpp @@ -59,9 +59,44 @@ namespace MWLua return Misc::StringUtils::format("Shader(%s, %s)", mShader->getName(), mShader->getFileName()); } - bool mQueuedAction = false; + enum { Action_None, Action_Enable, Action_Disable } mQueuedAction = Action_None; }; + template + auto getSetter(const Context& context) + { + return [context](const Shader& shader, const std::string& name, const T& value) { + context.mLuaManager->addAction(std::make_unique>(context.mLua, shader.mShader, name, value)); + }; + } + + template + auto getArraySetter(const Context& context) + { + return [context](const Shader& shader, const std::string& name, const sol::table& table) { + auto targetSize = MWBase::Environment::get().getWorld()->getPostProcessor()->getUniformSize(shader.mShader, name); + + if (!targetSize.has_value()) + throw std::runtime_error(Misc::StringUtils::format("Failed setting uniform array '%s'", name)); + + if (*targetSize != table.size()) + throw std::runtime_error(Misc::StringUtils::format("Mismatching uniform array size, got %zu expected %zu", table.size(), *targetSize)); + + std::vector values; + values.reserve(*targetSize); + + for (size_t i = 0; i < *targetSize; ++i) + { + sol::object obj = table[i+1]; + if (!obj.is()) + throw std::runtime_error("Invalid type for uniform array"); + values.push_back(obj.as()); + } + + context.mLuaManager->addAction(std::make_unique>>(context.mLua, shader.mShader, name, values)); + }; + } + sol::table initPostprocessingPackage(const Context& context) { sol::table api(context.mLua->sol(), sol::create); @@ -76,62 +111,64 @@ namespace MWLua pos = optPos.value(); if (shader.mShader && shader.mShader->isValid()) - shader.mQueuedAction = true; + shader.mQueuedAction = Shader::Action_Enable; context.mLuaManager->addAction( - [=] { MWBase::Environment::get().getWorld()->getPostProcessor()->enableTechnique(shader.mShader, pos); }, + [=, &shader] { + shader.mQueuedAction = Shader::Action_None; + + if (!MWBase::Environment::get().getWorld()->getPostProcessor()->enableTechnique(shader.mShader, pos)) + throw std::runtime_error("Failed enabling shader '" + shader.mShader->getName() + "'"); + }, "Enable shader " + (shader.mShader ? shader.mShader->getName() : "nil") ); }; shader["disable"] = [context](Shader& shader) { - shader.mQueuedAction = false; + shader.mQueuedAction = Shader::Action_Disable; context.mLuaManager->addAction( - [&] { MWBase::Environment::get().getWorld()->getPostProcessor()->disableTechnique(shader.mShader); }, + [&] { + shader.mQueuedAction = Shader::Action_None; + + if (!MWBase::Environment::get().getWorld()->getPostProcessor()->disableTechnique(shader.mShader)) + throw std::runtime_error("Failed disabling shader '" + shader.mShader->getName() + "'"); + }, "Disable shader " + (shader.mShader ? shader.mShader->getName() : "nil") ); }; shader["isEnabled"] = [](const Shader& shader) { - return shader.mQueuedAction; + if (shader.mQueuedAction == Shader::Action_Enable) + return true; + else if (shader.mQueuedAction == Shader::Action_Disable) + return false; + return MWBase::Environment::get().getWorld()->getPostProcessor()->isTechniqueEnabled(shader.mShader); }; - shader["setBool"] = [context](const Shader& shader, const std::string& name, bool value) - { - context.mLuaManager->addAction(std::make_unique>(context.mLua, shader.mShader, name, value)); - }; - - shader["setFloat"] = [context](const Shader& shader, const std::string& name, float value) - { - context.mLuaManager->addAction(std::make_unique>(context.mLua, shader.mShader, name, value)); - }; - - shader["setInt"] = [context](const Shader& shader, const std::string& name, int value) - { - context.mLuaManager->addAction(std::make_unique>(context.mLua, shader.mShader, name, value)); - }; + shader["setBool"] = getSetter(context); + shader["setFloat"] = getSetter(context); + shader["setInt"] = getSetter(context); + shader["setVector2"] = getSetter(context); + shader["setVector3"] = getSetter(context); + shader["setVector4"] = getSetter(context); - shader["setVector2"] = [context](const Shader& shader, const std::string& name, const osg::Vec2f& value) - { - context.mLuaManager->addAction(std::make_unique>(context.mLua, shader.mShader, name, value)); - }; + shader["setFloatArray"] = getArraySetter(context); + shader["setIntArray"] = getArraySetter(context); + shader["setVector2Array"] = getArraySetter(context); + shader["setVector3Array"] = getArraySetter(context); + shader["setVector4Array"] = getArraySetter(context); - shader["setVector3"] = [context](const Shader& shader, const std::string& name, const osg::Vec3f& value) + api["load"] = [](const std::string& name) { - context.mLuaManager->addAction(std::make_unique>(context.mLua, shader.mShader, name, value)); - }; + Shader shader{MWBase::Environment::get().getWorld()->getPostProcessor()->loadTechnique(name, false)}; - shader["setVector4"] = [context](const Shader& shader, const std::string& name, const osg::Vec4f& value) - { - context.mLuaManager->addAction(std::make_unique>(context.mLua, shader.mShader, name, value)); - }; + if (!shader.mShader || !shader.mShader->isValid()) + throw std::runtime_error(Misc::StringUtils::format("Failed loading shader '%s'", name)); - api["load"] = [](const std::string& name) - { - return Shader(MWBase::Environment::get().getWorld()->getPostProcessor()->loadTechnique(name, false)); + return shader; }; return LuaUtil::makeReadOnly(api); diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index a55fd3f38a..78016ceceb 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -406,6 +406,13 @@ namespace MWRender void PostProcessor::update(size_t frameId) { + while (!mQueuedTemplates.empty()) + { + mTemplates.push_back(std::move(mQueuedTemplates.back())); + + mQueuedTemplates.pop_back(); + } + updateLiveReload(); reloadIfRequired(); @@ -582,7 +589,7 @@ namespace MWRender if (uniform->mSamplerType) continue; if (auto type = uniform->getType()) - uniform->setUniform(node.mRootStateSet->getOrCreateUniform(uniform->mName, type.value())); + uniform->setUniform(node.mRootStateSet->getOrCreateUniform(uniform->mName.c_str(), *type, uniform->getNumElements())); } std::unordered_map renderTargetCache; @@ -643,7 +650,13 @@ namespace MWRender bool PostProcessor::enableTechnique(std::shared_ptr technique, std::optional location) { - if (!technique || technique->getName() == "main" || (location.has_value() && location.value() <= 0)) + if (!isEnabled()) + { + Log(Debug::Warning) << "PostProcessing disabled, cannot load technique '" << technique->getName() << "'"; + return false; + } + + if (!technique || Misc::StringUtils::ciEqual(technique->getName(), "main") || (location.has_value() && location.value() <= 0)) return false; disableTechnique(technique, false); @@ -753,14 +766,21 @@ namespace MWRender mHUDCamera->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); } - std::shared_ptr PostProcessor::loadTechnique(const std::string& name, bool insert) + std::shared_ptr PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame) { if (!isEnabled()) + { + Log(Debug::Warning) << "PostProcessing disabled, cannot load technique '" << name << "'"; return nullptr; + } - for (size_t i = 0; i < mTemplates.size(); ++i) - if (name == mTemplates[i]->getName()) - return mTemplates[i]; + for (const auto& technique : mTemplates) + if (Misc::StringUtils::ciEqual(technique->getName(), name)) + return technique; + + for (const auto& technique : mQueuedTemplates) + if (Misc::StringUtils::ciEqual(technique->getName(), name)) + return technique; auto technique = std::make_shared(*mVFS, *mRendering.getResourceSystem()->getImageManager(), name, mWidth, mHeight, mUBO, mNormalsSupported); @@ -769,8 +789,11 @@ namespace MWRender if (technique->getStatus() != fx::Technique::Status::File_Not_exists) technique->setLastModificationTime(std::filesystem::last_write_time(mTechniqueFileMap[technique->getName()])); - if (!insert) + if (!loadNextFrame) + { + mQueuedTemplates.push_back(technique); return technique; + } reloadMainPass(*technique); @@ -779,18 +802,6 @@ namespace MWRender return mTemplates.back(); } - void PostProcessor::addTemplate(std::shared_ptr technique) - { - if (!isEnabled()) - return; - - for (size_t i = 0; i < mTemplates.size(); ++i) - if (technique.get() == mTemplates[i].get()) - return; - - mTemplates.push_back(technique); - } - void PostProcessor::reloadTechniques() { if (!isEnabled()) @@ -810,7 +821,7 @@ namespace MWRender if (techniqueName.empty()) continue; - if ((&techniqueName != &techniqueStrings.front()) && Misc::StringUtils::ciEqual(techniqueName, "main")) + if ((&techniqueName != &techniqueStrings.front()) && techniqueName == "main") { Log(Debug::Warning) << "main.omwfx techniqued specified in chain, this is not allowed. technique file will be ignored if it exists."; continue; diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index a015abda4d..6663710ced 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -136,6 +136,16 @@ namespace MWRender (*it)->setValue(value); } + std::optional getUniformSize(std::shared_ptr technique, const std::string& name) + { + auto it = technique->findUniform(name); + + if (it == technique->getUniformMap().end()) + return std::nullopt; + + return (*it)->getNumElements(); + } + bool isTechniqueEnabled(const std::shared_ptr& technique) const; void setExteriorFlag(bool exterior) { mExteriorFlag = exterior; } @@ -144,9 +154,7 @@ namespace MWRender void toggleMode(); - std::shared_ptr loadTechnique(const std::string& name, bool insert=true); - - void addTemplate(std::shared_ptr technique); + std::shared_ptr loadTechnique(const std::string& name, bool loadNextFrame=true); bool isEnabled() const { return mUsePostProcessing && mEnabled; } @@ -192,6 +200,7 @@ namespace MWRender TechniqueList mTechniques; TechniqueList mTemplates; + TechniqueList mQueuedTemplates; std::unordered_map mTechniqueFileMap; diff --git a/apps/openmw_test_suite/fx/technique.cpp b/apps/openmw_test_suite/fx/technique.cpp index d10e1f1b37..9a382ad8ea 100644 --- a/apps/openmw_test_suite/fx/technique.cpp +++ b/apps/openmw_test_suite/fx/technique.cpp @@ -171,7 +171,7 @@ TestFile repeated_shared_block{R"( const auto& uniform = mTechnique->getUniformMap().front(); EXPECT_TRUE(uniform->mStatic); - EXPECT_FLOAT_EQ(uniform->mStep, 0.5f); + EXPECT_DOUBLE_EQ(uniform->mStep, 0.5); EXPECT_EQ(uniform->getDefault(), osg::Vec4f(0,0,0,0)); EXPECT_EQ(uniform->getMin(), osg::Vec4f(0,1,0,0)); EXPECT_EQ(uniform->getMax(), osg::Vec4f(0,0,1,0)); diff --git a/components/fx/technique.cpp b/components/fx/technique.cpp index 8b03810b73..8090a27b76 100644 --- a/components/fx/technique.cpp +++ b/components/fx/technique.cpp @@ -526,7 +526,7 @@ namespace fx error(Misc::StringUtils::format("redeclaration of uniform '%s'", std::string(mBlockName))); std::shared_ptr uniform = std::make_shared(); - Types::Uniform data; + Types::Uniform data = Types::Uniform(); while (!isNext() && !isNext()) { @@ -543,11 +543,6 @@ namespace fx static_assert(isVec || isFloat || isInt || isBool, "Unsupported type"); - std::optional step; - - if constexpr (isInt) - step = 1.0; - if (key == "default") { if constexpr (isVec) @@ -559,6 +554,15 @@ namespace fx else if constexpr (isBool) data.mDefault = parseBool(); } + else if (key == "size") + { + if constexpr (isBool) + error("bool arrays currently unsupported"); + + int size = parseInteger(); + if (size > 1) + data.mArray = std::vector(size); + } else if (key == "min") { if constexpr (isVec) @@ -582,7 +586,7 @@ namespace fx data.mMax = parseBool(); } else if (key == "step") - step = parseFloat(); + uniform->mStep = parseFloat(); else if (key == "static") uniform->mStatic = parseBool(); else if (key == "description") @@ -598,18 +602,28 @@ namespace fx else error(Misc::StringUtils::format("unexpected key '%s'", std::string{key})); - if (step) - uniform->mStep = step.value(); - expect(); } + if (data.isArray()) + uniform->mStatic = false; + uniform->mName = std::string(mBlockName); uniform->mData = data; uniform->mTechniqueName = mName; - if (auto cached = Settings::ShaderManager::get().getValue(mName, uniform->mName)) - uniform->setValue(cached.value()); + if (data.mArray) + { + if constexpr (!std::is_same_v) + { + if (auto cached = Settings::ShaderManager::get().getValue>(mName, uniform->mName)) + uniform->setValue(cached.value()); + } + } + else if (auto cached = Settings::ShaderManager::get().getValue(mName, uniform->mName)) + { + uniform->setValue(cached.value()); + } mDefinedUniforms.emplace_back(std::move(uniform)); } diff --git a/components/fx/types.hpp b/components/fx/types.hpp index f12810197f..40eee003f2 100644 --- a/components/fx/types.hpp +++ b/components/fx/types.hpp @@ -60,12 +60,24 @@ namespace fx struct Uniform { std::optional mValue; - T mDefault; + std::optional> mArray; + + T mDefault = {}; T mMin = std::numeric_limits::lowest(); T mMax = std::numeric_limits::max(); using value_type = T; + bool isArray() const + { + return mArray.has_value(); + } + + const std::vector& getArray() const + { + return *mArray; + } + T getValue() const { return mValue.value_or(mDefault); @@ -97,7 +109,7 @@ namespace fx bool mStatic = true; std::optional mSamplerType = std::nullopt; - double mStep; + double mStep = 1.0; Uniform_t mData; @@ -109,6 +121,11 @@ namespace fx return value.value_or(std::get>(mData).getValue()); } + size_t getNumElements() const + { + return std::visit([&](auto&& arg) { ;return arg.isArray() ? arg.getArray().size() : 1; }, mData); + } + template T getMin() const { @@ -137,8 +154,7 @@ namespace fx { arg.mValue = value; - if (mStatic) - Settings::ShaderManager::get().setValue(mTechniqueName, mName, value); + Settings::ShaderManager::get().setValue(mTechniqueName, mName, value); } else { @@ -147,6 +163,28 @@ namespace fx }, mData); } + template + void setValue(const std::vector& value) + { + std::visit([&, value](auto&& arg) { + using U = typename std::decay_t::value_type; + + if (!arg.isArray() || arg.getArray().size() != value.size()) + { + Log(Debug::Error) << "Attempting to set uniform array '" << mName << "' with mismatching array sizes"; + return; + } + + if constexpr (std::is_same_v) + { + arg.mArray = value; + Settings::ShaderManager::get().setValue(mTechniqueName, mName, value); + } + else + Log(Debug::Warning) << "Attempting to set uniform array '" << mName << "' with wrong type"; + }, mData); + } + void setUniform(osg::Uniform* uniform) { auto type = getType(); @@ -155,8 +193,14 @@ namespace fx std::visit([&](auto&& arg) { - const auto value = arg.getValue(); - uniform->set(value); + if (arg.isArray()) + { + for (size_t i = 0; i < arg.getArray().size(); ++i) + uniform->setElement(i, arg.getArray()[i]); + uniform->dirty(); + } + else + uniform->set(arg.getValue()); }, mData); } @@ -197,52 +241,53 @@ namespace fx } } - bool useUniform = (Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug || mStatic == false); - return std::visit([&](auto&& arg) -> std::optional { using T = typename std::decay_t::value_type; auto value = arg.getValue(); + const bool useUniform = arg.isArray() || (Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug || mStatic == false); + const std::string uname = arg.isArray() ? Misc::StringUtils::format("%s[%zu]", mName, arg.getArray().size()) : mName; + if constexpr (std::is_same_v) { if (useUniform) - return Misc::StringUtils::format("uniform vec2 %s;", mName); + return Misc::StringUtils::format("uniform vec2 %s;", uname); return Misc::StringUtils::format("const vec2 %s=vec2(%f,%f);", mName, value[0], value[1]); } else if constexpr (std::is_same_v) { if (useUniform) - return Misc::StringUtils::format("uniform vec3 %s;", mName); + return Misc::StringUtils::format("uniform vec3 %s;", uname); return Misc::StringUtils::format("const vec3 %s=vec3(%f,%f,%f);", mName, value[0], value[1], value[2]); } else if constexpr (std::is_same_v) { if (useUniform) - return Misc::StringUtils::format("uniform vec4 %s;", mName); + return Misc::StringUtils::format("uniform vec4 %s;", uname); return Misc::StringUtils::format("const vec4 %s=vec4(%f,%f,%f,%f);", mName, value[0], value[1], value[2], value[3]); } else if constexpr (std::is_same_v) { if (useUniform) - return Misc::StringUtils::format("uniform float %s;", mName); + return Misc::StringUtils::format("uniform float %s;", uname); return Misc::StringUtils::format("const float %s=%f;", mName, value); } else if constexpr (std::is_same_v) { if (useUniform) - return Misc::StringUtils::format("uniform int %s;", mName); + return Misc::StringUtils::format("uniform int %s;", uname); return Misc::StringUtils::format("const int %s=%i;", mName, value); } else if constexpr (std::is_same_v) { if (useUniform) - return Misc::StringUtils::format("uniform bool %s;", mName); + return Misc::StringUtils::format("uniform bool %s;", uname); return Misc::StringUtils::format("const bool %s=%s;", mName, value ? "true" : "false"); } diff --git a/components/settings/shadermanager.hpp b/components/settings/shadermanager.hpp index aec9fd4987..eaf10a3a34 100644 --- a/components/settings/shadermanager.hpp +++ b/components/settings/shadermanager.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include diff --git a/docs/source/reference/postprocessing/omwfx.rst b/docs/source/reference/postprocessing/omwfx.rst index 8354090b67..188fc9cebb 100644 --- a/docs/source/reference/postprocessing/omwfx.rst +++ b/docs/source/reference/postprocessing/omwfx.rst @@ -271,10 +271,10 @@ Below is an example of passing a value through a custom vertex shader to the fra } fragment pass { - omw_Out vec2 omw_TexCoord; + omw_In vec2 omw_TexCoord; // our custom output from the vertex shader is available - omw_Out float noise; + omw_In float noise; void main() { @@ -461,6 +461,14 @@ To use the uniform you can reference it in any pass, it should **not** be declar } } +You can use uniform arrays as well, but they are restricted to the `Lua API <../lua-scripting/openmw_postprocessing.html>`_ scripts. +These uniform blocks must be defined with the new ``size`` parameter. + +.. code-block:: none + + uniform_vec3 uArray { + size = 10; + } ``render_target`` ***************** diff --git a/docs/source/reference/postprocessing/overview.rst b/docs/source/reference/postprocessing/overview.rst index 05d5e428c0..8262895b72 100644 --- a/docs/source/reference/postprocessing/overview.rst +++ b/docs/source/reference/postprocessing/overview.rst @@ -7,7 +7,7 @@ Overview OpenMW supports a moddable post process framework for creating and controlling screenspace effects. This is integrated into OpenMW's Lua API, see -`reference <../lua-scripting/openmw_shader.html>`_ for details. +`reference <../lua-scripting/openmw_postprocessing.html>`_ for details. Basic concepts ============== diff --git a/files/lua_api/openmw/postprocessing.lua b/files/lua_api/openmw/postprocessing.lua index 36c96c2efc..18e2edd376 100644 --- a/files/lua_api/openmw/postprocessing.lua +++ b/files/lua_api/openmw/postprocessing.lua @@ -1,19 +1,18 @@ --- -- `openmw.postprocessing` is an interface to postprocessing shaders. -- Can be used only by local scripts, that are attached to a player. --- @module shader +-- @module postprocessing -- @usage local postprocessing = require('openmw.postprocessing') - --- -- Load a shader and return its handle. -- @function [parent=#postprocessing] load -- @param #string name Name of the shader without its extension -- @return #Shader -- @usage --- If the shader exists and compiles, the shader will still be off by default. --- It must be enabled to see its effect. +-- -- If the shader exists and compiles, the shader will still be off by default. +-- -- It must be enabled to see its effect. -- local vignetteShader = postprocessing.load('vignette') --- @@ -72,21 +71,62 @@ -- @function [parent=#Shader] setVector2 -- @param self -- @param #string name Name of uniform --- @param #Vector2 value Value of uniform. +-- @param openmw.util#Vector2 value Value of uniform. --- -- Set a non static Vector3 shader variable. -- @function [parent=#Shader] setVector3 -- @param self -- @param #string name Name of uniform --- @param #Vector3 value Value of uniform. +-- @param openmw.util#Vector3 value Value of uniform. --- -- Set a non static Vector4 shader variable. -- @function [parent=#Shader] setVector4 -- @param self -- @param #string name Name of uniform --- @param #Vector4 value Value of uniform. +-- @param openmw.util#Vector4 value Value of uniform. + +--- +-- Set a non static integer array shader variable. +-- @function [parent=#Shader] setIntArray +-- @param self +-- @param #string name Name of uniform +-- @param #table array Contains equal number of #number elements as the uniform array. + +--- +-- Set a non static float array shader variable. +-- @function [parent=#Shader] setFloatArray +-- @param self +-- @param #string name Name of uniform +-- @param #table array Contains equal number of #number elements as the uniform array. + +--- +-- Set a non static Vector2 array shader variable. +-- @function [parent=#Shader] setVector2Array +-- @param self +-- @param #string name Name of uniform +-- @param #table array Contains equal number of @{openmw.util#Vector2} elements as the uniform array. + +--- +-- Set a non static Vector3 array shader variable. +-- @function [parent=#Shader] setVector3Array +-- @param self +-- @param #string name Name of uniform +-- @param #table array Contains equal number of @{openmw.util#Vector3} elements as the uniform array. +--- +-- Set a non static Vector4 array shader variable. +-- @function [parent=#Shader] setVector4Array +-- @param self +-- @param #string name Name of uniform +-- @param #table array Contains equal number of @{openmw.util#Vector4} elements as the uniform array. +-- @usage +-- -- Setting an array +-- local shader = postprocessing.load('godrays') +-- -- Toggle shader on +-- shader:enable() +-- -- Set new array uniform which was defined with length 2 +-- shader:setVector4Array('myArray', { util.vector4(1,0,0,1), util.vector4(1,0,1,1) }) return nil