From 71c30a31df20242a1554c3c632172a96336a081d Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Sat, 3 Apr 2021 22:25:13 -0700 Subject: [PATCH] in-game settings, some require restart --- apps/openmw/mwgui/settingswindow.cpp | 90 +++++++++++- apps/openmw/mwgui/settingswindow.hpp | 9 +- apps/openmw/mwrender/renderingmanager.cpp | 20 ++- components/sceneutil/lightmanager.cpp | 136 +++++++++++------- components/sceneutil/lightmanager.hpp | 11 +- .../reference/modding/settings/shaders.rst | 4 +- files/mygui/openmw_settings_window.layout | 89 ++++++++++++ files/shaders/lighting.glsl | 81 +++++++---- files/shaders/water_fragment.glsl | 7 +- 9 files changed, 352 insertions(+), 95 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 538b3db5ed..a93400490c 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -107,7 +109,7 @@ namespace namespace MWGui { - void SettingsWindow::configureWidgets(MyGUI::Widget* widget) + void SettingsWindow::configureWidgets(MyGUI::Widget* widget, bool init) { MyGUI::EnumeratorWidgetPtr widgets = widget->getEnumerator(); while (widgets.next()) @@ -121,7 +123,8 @@ namespace MWGui getSettingCategory(current)) ? "#{sOn}" : "#{sOff}"; current->castType()->setCaptionWithReplacing(initialValue); - current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + if (init) + current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); } if (type == sliderType) { @@ -141,6 +144,12 @@ namespace MWGui ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits; valueStr = ss.str(); } + else if (valueType == "Float") + { + std::stringstream ss; + ss << std::fixed << std::setprecision(2) << value; + valueStr = ss.str(); + } else valueStr = MyGUI::utility::toString(int(value)); @@ -155,12 +164,13 @@ namespace MWGui valueStr = MyGUI::utility::toString(value); scroll->setScrollPosition(value); } - scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + if (init) + scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); if (scroll->getVisible()) updateSliderLabel(scroll, valueStr); } - configureWidgets(current); + configureWidgets(current, init); } } @@ -187,7 +197,7 @@ namespace MWGui getWidget(unusedSlider, widgetName); unusedSlider->setVisible(false); - configureWidgets(mMainWidget); + configureWidgets(mMainWidget, true); setTitle("#{sOptions}"); @@ -204,6 +214,9 @@ namespace MWGui getWidget(mControllerSwitch, "ControllerButton"); getWidget(mWaterTextureSize, "WaterTextureSize"); getWidget(mWaterReflectionDetail, "WaterReflectionDetail"); + getWidget(mLightingMethodButton, "LightingMethodButton"); + getWidget(mMaxLightsSlider, "MaxLightsSlider"); + getWidget(mLightsResetButton, "LightsResetButton"); #ifndef WIN32 // hide gamma controls since it currently does not work under Linux @@ -229,6 +242,9 @@ namespace MWGui mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged); mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged); + mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodChanged); + mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked); + mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked); mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked); @@ -272,6 +288,21 @@ namespace MWGui waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail)); mWaterReflectionDetail->setIndexSelected(waterReflectionDetail); + auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders")); + switch (lightingMethod) + { + case SceneUtil::LightingMethod::Undefined: + case SceneUtil::LightingMethod::FFP: + mLightingMethodButton->setIndexSelected(0); + break; + case SceneUtil::LightingMethod::PerObjectUniform: + mLightingMethodButton->setIndexSelected(1); + break; + case SceneUtil::LightingMethod::SingleUBO: + mLightingMethodButton->setIndexSelected(2); + break; + } + mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video")); mKeyboardSwitch->setStateSelected(true); @@ -358,6 +389,49 @@ namespace MWGui apply(); } + void SettingsWindow::onLightsResetButtonClicked(MyGUI::Widget* _sender) + { + std::vector buttons = {"#{sYes}", "#{sNo}"}; + std::string message = "This will reset all lighting settings to default, some changes will require a restart. Would you like to continue?"; + MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons, true); + int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if (selectedButton == 1 || selectedButton == -1) + return; + + Settings::Manager::setString("lighting method", "Shaders", "shaders compatibility"); + Settings::Manager::setFloat("light bounds multiplier", "Shaders", 1.75); + Settings::Manager::setInt("maximum light distance", "Shaders", 8192); + Settings::Manager::setFloat("light fade start", "Shaders", 0.85); + Settings::Manager::setFloat("minimum interior brightness", "Shaders", 0.1); + Settings::Manager::setInt("max lights", "Shaders", 8); + + mLightingMethodButton->setIndexSelected(1); + + apply(); + configureWidgets(mMainWidget, false); + } + + void SettingsWindow::onLightingMethodChanged(MyGUI::ComboBox* _sender, size_t pos) + { + std::string setting; + auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(_sender->getItemNameAt(pos)); + switch (lightingMethod) + { + case SceneUtil::LightingMethod::FFP: + setting = "legacy"; + break; + case SceneUtil::LightingMethod::Undefined: + case SceneUtil::LightingMethod::PerObjectUniform: + setting = "shaders compatibility"; + break; + case SceneUtil::LightingMethod::SingleUBO: + setting = "shaders"; + break; + } + Settings::Manager::setString("lighting method", "Shaders", setting); + apply(); + } + void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender) { std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On"); @@ -460,6 +534,12 @@ namespace MWGui ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits; valueStr = ss.str(); } + else if (valueType == "Float") + { + std::stringstream ss; + ss << std::fixed << std::setprecision(2) << value; + valueStr = ss.str(); + } else valueStr = MyGUI::utility::toString(int(value)); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 6f25dd1143..3ec142731e 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -35,6 +35,10 @@ namespace MWGui MyGUI::ComboBox* mWaterTextureSize; MyGUI::ComboBox* mWaterReflectionDetail; + MyGUI::ComboBox* mLightingMethodButton; + MyGUI::ScrollBar* mMaxLightsSlider; + MyGUI::Button* mLightsResetButton; + // controls MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; @@ -55,6 +59,9 @@ namespace MWGui void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos); void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos); + void onLightsResetButtonClicked(MyGUI::Widget* _sender); + void onLightingMethodChanged(MyGUI::ComboBox* _sender, size_t pos); + void onRebindAction(MyGUI::Widget* _sender); void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel); void onResetDefaultBindings(MyGUI::Widget* _sender); @@ -66,7 +73,7 @@ namespace MWGui void apply(); - void configureWidgets(MyGUI::Widget* widget); + void configureWidgets(MyGUI::Widget* widget, bool init); void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value); void layoutControlsBox(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c14431518d..e5c5ace9d5 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -52,6 +52,7 @@ #include "../mwgui/loadingscreen.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwmechanics/actorutil.hpp" #include "sky.hpp" #include "effectmanager.hpp" @@ -224,8 +225,7 @@ namespace MWRender resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod()); resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod()); - if (sceneRoot->getLightingMethod() != SceneUtil::LightingMethod::FFP) - mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f); + mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f); sceneRoot->setLightingMask(Mask_Lighting); mSceneRoot = sceneRoot; @@ -1144,9 +1144,25 @@ namespace MWRender else if (it->first == "General" && (it->second == "texture filter" || it->second == "texture mipmap" || it->second == "anisotropy")) + { updateTextureFiltering(); + } else if (it->first == "Water") + { mWater->processChangedSettings(changed); + } + else if (it->first == "Shaders" && it->second == "minimum interior brightness") + { + mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f); + if (MWMechanics::getPlayer().getCell()) + configureAmbient(MWMechanics::getPlayer().getCell()->getCell()); + } + else if (it->first == "Shaders" && (it->second == "light bounds multiplier" || + it->second == "maximum light distance" || + it->second == "light fade start")) + { + static_cast(getLightRoot())->processChangedSettings(changed); + } } } diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index f20e28a7e3..8e4c9910f8 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -10,8 +10,6 @@ #include -#include - #include namespace @@ -41,6 +39,43 @@ namespace { light->setUserValue("radius", value); } + + void configurePosition(osg::Matrixf& mat, const osg::Vec4& pos) + { + mat(0, 0) = pos.x(); + mat(0, 1) = pos.y(); + mat(0, 2) = pos.z(); + } + + void configureAmbient(osg::Matrixf& mat, const osg::Vec4& color) + { + mat(1, 0) = color.r(); + mat(1, 1) = color.g(); + mat(1, 2) = color.b(); + } + + void configureDiffuse(osg::Matrixf& mat, const osg::Vec4& color) + { + mat(2, 0) = color.r(); + mat(2, 1) = color.g(); + mat(2, 2) = color.b(); + } + + void configureSpecular(osg::Matrixf& mat, const osg::Vec4& color) + { + mat(3, 0) = color.r(); + mat(3, 1) = color.g(); + mat(3, 2) = color.b(); + mat(3, 3) = color.a(); + } + + void configureAttenuation(osg::Matrixf& mat, float c, float l, float q, float r) + { + mat(0, 3) = c; + mat(1, 3) = l; + mat(2, 3) = q; + mat(3, 3) = r; + } } namespace SceneUtil @@ -206,11 +241,12 @@ namespace SceneUtil } case LightingMethod::PerObjectUniform: { - stateset->addUniform(new osg::Uniform("LightBuffer[0].diffuse", light->getDiffuse()), mode); - stateset->addUniform(new osg::Uniform("LightBuffer[0].ambient", light->getAmbient()), mode); - stateset->addUniform(new osg::Uniform("LightBuffer[0].specular", light->getSpecular()), mode); - stateset->addUniform(new osg::Uniform("LightBuffer[0].position", light->getPosition()), mode); - + osg::Matrixf lightMat; + configurePosition(lightMat, light->getPosition()); + configureAmbient(lightMat, light->getAmbient()); + configureDiffuse(lightMat, light->getDiffuse()); + configureSpecular(lightMat, light->getSpecular()); + stateset->addUniform(new osg::Uniform("LightBuffer", lightMat), mode); break; } case LightingMethod::SingleUBO: @@ -381,14 +417,20 @@ namespace SceneUtil void apply(osg::State &state) const override { + auto* lightUniform = mLightManager->getStateSet()->getUniform("LightBuffer"); for (size_t i = 0; i < mLights.size(); ++i) { auto light = mLights[i]; - mLightManager->getLightUniform(i+1, LightManager::UniformKey::Diffuse)->set(light->getDiffuse()); - mLightManager->getLightUniform(i+1, LightManager::UniformKey::Ambient)->set(light->getAmbient()); - mLightManager->getLightUniform(i+1, LightManager::UniformKey::Attenuation)->set(osg::Vec4(light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), getLightRadius(light))); - mLightManager->getLightUniform(i+1, LightManager::UniformKey::Position)->set(light->getPosition() * state.getInitialViewMatrix()); + osg::Matrixf lightMat; + + configurePosition(lightMat, light->getPosition() * state.getInitialViewMatrix()); + configureAmbient(lightMat, light->getAmbient()); + configureDiffuse(lightMat, light->getDiffuse()); + configureAttenuation(lightMat, light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), getLightRadius(light)); + + lightUniform->setElement(i+1, lightMat); } + lightUniform->dirty(); } private: @@ -589,10 +631,12 @@ namespace SceneUtil { if (mLightManager->getLightingMethod() == LightingMethod::PerObjectUniform) { - mLightManager->getLightUniform(0, LightManager::UniformKey::Diffuse)->set(sun->getDiffuse()); - mLightManager->getLightUniform(0, LightManager::UniformKey::Ambient)->set(sun->getAmbient()); - mLightManager->getLightUniform(0, LightManager::UniformKey::Specular)->set(sun->getSpecular()); - mLightManager->getLightUniform(0, LightManager::UniformKey::Position)->set(sun->getPosition() * (*cv->getCurrentRenderStage()->getInitialViewMatrix())); + osg::Matrixf lightMat; + configurePosition(lightMat, sun->getPosition() * (*cv->getCurrentRenderStage()->getInitialViewMatrix())); + configureAmbient(lightMat, sun->getAmbient()); + configureDiffuse(lightMat, sun->getDiffuse()); + configureSpecular(lightMat, sun->getSpecular()); + mLightManager->getStateSet()->getUniform("LightBuffer")->setElement(0, lightMat); } else { @@ -760,14 +804,7 @@ namespace SceneUtil lightingMethod = LightingMethod::PerObjectUniform; } - mPointLightRadiusMultiplier = std::clamp(Settings::Manager::getFloat("light bounds multiplier", "Shaders"), 0.f, 10.f); - - mPointLightFadeEnd = std::max(0.f, Settings::Manager::getFloat("maximum light distance", "Shaders")); - if (mPointLightFadeEnd > 0) - { - mPointLightFadeStart = std::clamp(Settings::Manager::getFloat("light fade start", "Shaders"), 0.f, 1.f); - mPointLightFadeStart = mPointLightFadeEnd * mPointLightFadeStart; - } + updateSettings(); osg::GLExtensions* exts = osg::GLExtensions::Get(0, false); bool supportsUBO = exts && exts->isUniformBufferObjectSupported; @@ -839,9 +876,6 @@ namespace SceneUtil { Shader::ShaderManager::DefineMap defines; - bool ffp = usingFFP(); - - defines["ffpLighting"] = ffp ? "1" : "0"; defines["maxLights"] = std::to_string(getMaxLights()); defines["maxLightsInScene"] = std::to_string(getMaxLightsInScene()); defines["lightingModel"] = std::to_string(static_cast(mLightingMethod)); @@ -852,6 +886,26 @@ namespace SceneUtil return defines; } + void LightManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + updateSettings(); + } + + void LightManager::updateSettings() + { + if (getLightingMethod() == LightingMethod::FFP) + return; + + mPointLightRadiusMultiplier = std::clamp(Settings::Manager::getFloat("light bounds multiplier", "Shaders"), 0.f, 5.f); + + mPointLightFadeEnd = std::max(0.f, Settings::Manager::getFloat("maximum light distance", "Shaders")); + if (mPointLightFadeEnd > 0) + { + mPointLightFadeStart = std::clamp(Settings::Manager::getFloat("light fade start", "Shaders"), 0.f, 1.f); + mPointLightFadeStart = mPointLightFadeEnd * mPointLightFadeStart; + } + } + void LightManager::initFFP(int targetLights) { setLightingMethod(LightingMethod::FFP); @@ -866,38 +920,15 @@ namespace SceneUtil auto* stateset = getOrCreateStateSet(); setLightingMethod(LightingMethod::PerObjectUniform); - setMaxLights(std::max(2, targetLights)); + setMaxLights(std::clamp(targetLights, 2, 64)); - mLightUniforms.resize(getMaxLights()+1); - for (size_t i = 0; i < mLightUniforms.size(); ++i) - { - osg::ref_ptr udiffuse = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].diffuse").c_str()); - osg::ref_ptr uspecular = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].specular").c_str()); - osg::ref_ptr uambient = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].ambient").c_str()); - osg::ref_ptr uposition = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].position").c_str()); - osg::ref_ptr uattenuation = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].attenuation").c_str()); - - mLightUniforms[i].emplace(UniformKey::Diffuse, udiffuse); - mLightUniforms[i].emplace(UniformKey::Ambient, uambient); - mLightUniforms[i].emplace(UniformKey::Specular, uspecular); - mLightUniforms[i].emplace(UniformKey::Position, uposition); - mLightUniforms[i].emplace(UniformKey::Attenuation, uattenuation); - - stateset->addUniform(udiffuse); - stateset->addUniform(uambient); - stateset->addUniform(uposition); - stateset->addUniform(uattenuation); - - // specular isn't used besides sun, complete waste to upload it - if (i == 0) - stateset->addUniform(uspecular); - } + stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", getMaxLights() + 1)); } void LightManager::initSingleUBO(int targetLights) { setLightingMethod(LightingMethod::SingleUBO); - setMaxLights(std::clamp(targetLights, 2, getMaxLightsInScene() / 2)); + setMaxLights(std::clamp(targetLights, 2, 64)); for (int i = 0; i < 2; ++i) { @@ -912,7 +943,6 @@ namespace SceneUtil getOrCreateStateSet()->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON); } - void LightManager::setLightingMethod(LightingMethod method) { mLightingMethod = method; diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index cd8f2d3127..2fdc14a8da 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -14,6 +14,8 @@ #include +#include + namespace osgUtil { class CullVisitor; @@ -172,10 +174,10 @@ namespace SceneUtil auto& getLightBuffer(size_t frameNum) { return mLightBuffers[frameNum%2]; } - auto& getLightUniform(int index, UniformKey key) { return mLightUniforms[index][key]; } - std::map getLightDefines() const; + void processChangedSettings(const Settings::CategorySettingVector& changed); + private: friend class LightManagerStateAttribute; friend class LightManagerCullCallback; @@ -184,6 +186,8 @@ namespace SceneUtil void initPerObjectUniform(int targetLights); void initSingleUBO(int targetLights); + void updateSettings(); + void setLightingMethod(LightingMethod method); void setMaxLights(int value); @@ -212,9 +216,6 @@ namespace SceneUtil using LightIndexMap = std::unordered_map; LightIndexMap mLightIndexMaps[2]; - using UniformMap = std::vector>>; - UniformMap mLightUniforms; - std::unique_ptr mStateSetGenerator; LightingMethod mLightingMethod; diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index be38bd3ec4..0537332a81 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -185,7 +185,7 @@ light bounds multiplier ----------------------- :Type: float -:Range: 0.0-10.0 +:Range: 0.0-5.0 :Default: 1.75 Controls the bounding sphere radius of point lights, which is used to determine @@ -228,7 +228,7 @@ max lights ---------- :Type: integer -:Range: >=2 +:Range: 2-64 :Default: 8 Sets the maximum number of lights that each object can receive lighting from. diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index babb5c28f9..ef5d4a6cb4 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -456,6 +456,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +