From 4ba473b6840aa3b9b4fc2ecb9a8b3580837ed86d Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Mon, 5 Apr 2021 10:43:17 -0700 Subject: [PATCH] Finalize settings, torch fix --- apps/launcher/advancedpage.cpp | 9 ++ apps/opencs/model/world/data.cpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 69 ++++---- apps/openmw/mwgui/settingswindow.hpp | 5 +- apps/openmw/mwrender/animation.cpp | 13 +- apps/openmw/mwrender/animation.hpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 28 +++- components/sceneutil/lightcontroller.cpp | 3 +- components/sceneutil/lightmanager.cpp | 79 ++++++++- components/sceneutil/lightmanager.hpp | 33 ++-- components/sceneutil/lightutil.cpp | 3 +- components/sceneutil/lightutil.hpp | 2 +- files/mygui/openmw_settings_window.layout | 185 ++++++++++++---------- files/shaders/lighting.glsl | 8 +- files/ui/advancedpage.ui | 30 ++++ 15 files changed, 318 insertions(+), 152 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index d82dd1be2..0aa378805 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -124,6 +125,11 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera"))); + + auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(mEngineSettings.getString("lighting method", "Shaders")); + if (lightingMethod == SceneUtil::LightingMethod::Undefined) + lightingMethod = SceneUtil::LightingMethod::PerObjectUniform; + lightingMethodComboBox->setCurrentIndex(static_cast(lightingMethod)); } // Camera @@ -246,6 +252,9 @@ void Launcher::AdvancedPage::saveSettings() { mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance)); } + + auto lightingMethodStr = SceneUtil::LightManager::getLightingMethodString(static_cast(lightingMethodComboBox->currentIndex())); + mEngineSettings.setString("lighting method", "Shaders", lightingMethodStr); } // Camera diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 31778b904..319334c9b 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -83,7 +83,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat defines["clamp"] = "1"; // Clamp lighting defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind defines["radialFog"] = "0"; - defines["ffpLighting"] = "1"; + defines["lightingModel"] = "0"; for (const auto& define : shadowDefines) defines[define.first] = define.second; mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index a93400490..4d3b14152 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include @@ -214,9 +213,9 @@ namespace MWGui getWidget(mControllerSwitch, "ControllerButton"); getWidget(mWaterTextureSize, "WaterTextureSize"); getWidget(mWaterReflectionDetail, "WaterReflectionDetail"); - getWidget(mLightingMethodButton, "LightingMethodButton"); - getWidget(mMaxLightsSlider, "MaxLightsSlider"); + getWidget(mLightingMethodText, "LightingMethodText"); getWidget(mLightsResetButton, "LightsResetButton"); + getWidget(mLightSettingOverlay, "LightSettingOverlay"); #ifndef WIN32 // hide gamma controls since it currently does not work under Linux @@ -242,7 +241,6 @@ 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); @@ -289,18 +287,36 @@ namespace MWGui mWaterReflectionDetail->setIndexSelected(waterReflectionDetail); auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders")); - switch (lightingMethod) + mLightingMethodText->setCaption(SceneUtil::LightManager::getLightingMethodString(lightingMethod)); + + if (lightingMethod == SceneUtil::LightingMethod::FFP || !Settings::Manager::getBool("force shaders", "Shaders")) { - 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; + std::string warningText = + "Unavailable with current settings." + "\n\nEnable \"force shaders\" and use either the \"shaders\" or \"shaders compatibility\" lighting method."; + MyGUI::Widget* parent = mLightSettingOverlay->getParent(); + mLightSettingOverlay->setEnabled(false); + mLightSettingOverlay->setAlpha(0.8); + parent->setUserString("ToolTipType", "Layout"); + parent->setUserString("ToolTipLayout", "TextToolTip"); + parent->setUserString("Caption_Text", warningText); + parent->setEnabled(true); + } + else + { + std::string captionText; + if (lightingMethod == SceneUtil::LightingMethod::FFP) + captionText = + "Emulates fixed function pipeline lighting, advanced light settings are disabled when this mode is active"; + else if (lightingMethod == SceneUtil::LightingMethod::PerObjectUniform) + captionText = + "Removes limit of 8 lights per object, fixes lighting attenuation, and enables groundcover lighting and light fade." + "\n\nDesigned for compatibility across hardware, and is not meant for large max light counts."; + else if (lightingMethod == SceneUtil::LightingMethod::SingleUBO) + captionText = + "Removes limit of 8 lights per object, fixes lighting attenuation, and enables groundcover lighting and light fade." + "\n\nDesigned for more modern hardware and large max light counts."; + mLightingMethodText->setUserString("Caption_Text", captionText); } mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video")); @@ -405,33 +421,10 @@ namespace MWGui 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"); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 3ec142731..219c55c96 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -35,8 +35,8 @@ namespace MWGui MyGUI::ComboBox* mWaterTextureSize; MyGUI::ComboBox* mWaterReflectionDetail; - MyGUI::ComboBox* mLightingMethodButton; - MyGUI::ScrollBar* mMaxLightsSlider; + MyGUI::Widget* mLightSettingOverlay; + MyGUI::TextBox* mLightingMethodText; MyGUI::Button* mLightsResetButton; // controls @@ -60,7 +60,6 @@ namespace MWGui 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); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d8c759935..79447676e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -499,6 +499,11 @@ namespace MWRender mAlpha = alpha; } + void setLightSource(const osg::ref_ptr& lightSource) + { + mLightSource = lightSource; + } + protected: void setDefaults(osg::StateSet* stateset) override { @@ -517,14 +522,17 @@ namespace MWRender stateset->addUniform(new osg::Uniform("colorMode", 0), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } - void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override + void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override { osg::Material* material = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); material->setAlpha(osg::Material::FRONT_AND_BACK, mAlpha); + if (mLightSource) + mLightSource->setActorFade(mAlpha); } private: float mAlpha; + osg::ref_ptr mLightSource; }; struct Animation::AnimSource @@ -1613,7 +1621,7 @@ namespace MWRender { bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); - SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior); + mExtraLightSource = SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior); } void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture) @@ -1763,6 +1771,7 @@ namespace MWRender if (mTransparencyUpdater == nullptr) { mTransparencyUpdater = new TransparencyUpdater(alpha); + mTransparencyUpdater->setLightSource(mExtraLightSource); mObjectRoot->addCullCallback(mTransparencyUpdater); } else diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 04c5825c9..213a4f704 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -278,6 +278,7 @@ protected: osg::ref_ptr mGlowLight; osg::ref_ptr mGlowUpdater; osg::ref_ptr mTransparencyUpdater; + osg::ref_ptr mExtraLightSource; float mAlpha; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e5c5ace9d..1ee7b2132 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1154,14 +1154,36 @@ namespace MWRender 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()) + if (MWMechanics::getPlayer().isInCell()) 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")) + it->second == "light fade start" || + it->second == "max lights")) { - static_cast(getLightRoot())->processChangedSettings(changed); + auto* lightManager = static_cast(getLightRoot()); + lightManager->processChangedSettings(changed); + + if (it->second == "max lights" && !lightManager->usingFFP()) + { + mViewer->stopThreading(); + + lightManager->updateMaxLights(); + + auto defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines(); + for (auto& define : lightManager->getLightDefines()) + defines[define.first] = define.second; + mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); + + mSceneRoot->removeUpdateCallback(mStateUpdater); + mStateUpdater = new StateUpdater; + mSceneRoot->addUpdateCallback(mStateUpdater); + mStateUpdater->setFogEnd(mViewDistance); + updateAmbient(); + + mViewer->startThreading(); + } } } } diff --git a/components/sceneutil/lightcontroller.cpp b/components/sceneutil/lightcontroller.cpp index c759fabc7..cc320aecf 100644 --- a/components/sceneutil/lightcontroller.cpp +++ b/components/sceneutil/lightcontroller.cpp @@ -62,7 +62,8 @@ namespace SceneUtil mPhase = mPhase <= 0.5f ? 1.f : 0.25f; } - static_cast(node)->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness); + auto* lightSource = static_cast(node); + lightSource->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness * lightSource->getActorFade()); traverse(node, nv); } diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 8e4c9910f..30d2e2415 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -415,6 +415,11 @@ namespace SceneUtil META_StateAttribute(NifOsg, LightStateAttributePerObjectUniform, osg::StateAttribute::LIGHT) + void resize(int numLights) + { + mLights.resize(std::min(static_cast(numLights), mLights.size())); + } + void apply(osg::State &state) const override { auto* lightUniform = mLightManager->getStateSet()->getUniform("LightBuffer"); @@ -511,6 +516,9 @@ namespace SceneUtil uOldCount->get(oldCount); + // max lights count can change during runtime + oldCount = std::min(mLightManager->getMaxLights(), oldCount); + auto& lightData = mLightManager->getLightIndexMap(frameNum); for (int i = 0; i < oldCount; ++i) @@ -779,6 +787,14 @@ namespace SceneUtil return LightingMethod::Undefined; } + std::string LightManager::getLightingMethodString(LightingMethod method) + { + for (const auto& p : LightManager::mLightingMethodSettingMap) + if (p.second == method) + return p.first; + return ""; + } + LightManager::LightManager(bool ffp) : mStartLight(0) , mLightingMask(~0u) @@ -891,6 +907,61 @@ namespace SceneUtil updateSettings(); } + void LightManager::updateMaxLights() + { + if (usingFFP()) + return; + + setMaxLights(std::clamp(Settings::Manager::getInt("max lights", "Shaders"), mMaxLightsLowerLimit, mMaxLightsUpperLimit)); + + if (getLightingMethod() == LightingMethod::PerObjectUniform) + { + auto* prevUniform = getStateSet()->getUniform("LightBuffer"); + osg::ref_ptr newUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", getMaxLights() + 1); + + for (int i = 0; i < getMaxLights() + 1; ++i) + { + osg::Matrixf prevLightData; + prevUniform->getElement(i, prevLightData); + newUniform->setElement(i, prevLightData); + } + + getStateSet()->removeUniform(prevUniform); + getStateSet()->addUniform(newUniform); + + for (int i = 0; i < 2; ++i) + { + for (auto& pair : mStateSetCache[i]) + static_cast(pair.second->getAttribute(osg::StateAttribute::LIGHT))->resize(getMaxLights()); + mStateSetCache[i].clear(); + } + } + else + { + for (int i = 0; i < 2; ++i) + { + for (auto& pair : mStateSetCache[i]) + { + auto& stateset = pair.second; + osg::Uniform* uOldArray = stateset->getUniform("PointLightIndex"); + osg::Uniform* uOldCount = stateset->getUniform("PointLightCount"); + + int prevCount; + uOldCount->get(prevCount); + int newCount = std::min(getMaxLights(), prevCount); + uOldCount->set(newCount); + + osg::ref_ptr newArray = uOldArray->getIntArray(); + newArray->resize(newCount); + + stateset->removeUniform(uOldArray); + stateset->addUniform(new osg::Uniform("PointLightIndex", newArray)); + } + mStateSetCache[i].clear(); + } + } + } + void LightManager::updateSettings() { if (getLightingMethod() == LightingMethod::FFP) @@ -920,7 +991,7 @@ namespace SceneUtil auto* stateset = getOrCreateStateSet(); setLightingMethod(LightingMethod::PerObjectUniform); - setMaxLights(std::clamp(targetLights, 2, 64)); + setMaxLights(std::clamp(targetLights, mMaxLightsLowerLimit, LightManager::mMaxLightsUpperLimit)); stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", getMaxLights() + 1)); } @@ -928,7 +999,7 @@ namespace SceneUtil void LightManager::initSingleUBO(int targetLights) { setLightingMethod(LightingMethod::SingleUBO); - setMaxLights(std::clamp(targetLights, 2, 64)); + setMaxLights(std::clamp(targetLights, mMaxLightsLowerLimit, LightManager::mMaxLightsUpperLimit)); for (int i = 0; i < 2; ++i) { @@ -1000,7 +1071,7 @@ namespace SceneUtil getLightIndexMap(frameNum).clear(); mLights.clear(); mLightsInViewSpace.clear(); - + return; // Do an occasional cleanup for orphaned lights. for (int i = 0; i < 2; ++i) { @@ -1133,6 +1204,7 @@ namespace SceneUtil LightSource::LightSource() : mRadius(0.f) + , mActorFade(1.f) { setUpdateCallback(new CollectLightCallback); mId = sLightId++; @@ -1141,6 +1213,7 @@ namespace SceneUtil LightSource::LightSource(const LightSource ©, const osg::CopyOp ©op) : osg::Node(copy, copyop) , mRadius(copy.mRadius) + , mActorFade(copy.mActorFade) { mId = sLightId++; diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index 2fdc14a8d..27716861f 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -29,8 +29,8 @@ namespace SceneUtil enum class LightingMethod { FFP, - SingleUBO, PerObjectUniform, + SingleUBO, Undefined }; @@ -54,6 +54,8 @@ namespace SceneUtil int mId; + float mActorFade; + public: META_Node(SceneUtil, LightSource) @@ -73,6 +75,16 @@ namespace SceneUtil mRadius = radius; } + void setActorFade(float alpha) + { + mActorFade = alpha; + } + + float getActorFade() const + { + return mActorFade; + } + /// Get the osg::Light safe for modification in the given frame. /// @par May be used externally to animate the light's color/attenuation properties, /// and is used internally to synchronize the light's position with the position of the LightSource. @@ -104,15 +116,8 @@ namespace SceneUtil public: static bool isValidLightingModelString(const std::string& value); static LightingMethod getLightingMethodFromString(const std::string& value); - - enum class UniformKey - { - Diffuse, - Ambient, - Specular, - Position, - Attenuation - }; + /// Returns string as used in settings file, or the empty string if the method is undefined + static std::string getLightingMethodString(LightingMethod method); struct LightSourceTransform { @@ -178,10 +183,10 @@ namespace SceneUtil void processChangedSettings(const Settings::CategorySettingVector& changed); - private: - friend class LightManagerStateAttribute; - friend class LightManagerCullCallback; + /// Not thread safe, it is the responsibility of the caller to stop/start threading on the viewer + void updateMaxLights(); + private: void initFFP(int targetLights); void initPerObjectUniform(int targetLights); void initSingleUBO(int targetLights); @@ -226,6 +231,8 @@ namespace SceneUtil int mMaxLights; + static constexpr auto mMaxLightsLowerLimit = 2; + static constexpr auto mMaxLightsUpperLimit = 64; static constexpr auto mFFPMaxLights = 8; static const std::unordered_map mLightingMethodSettingMap; diff --git a/components/sceneutil/lightutil.cpp b/components/sceneutil/lightutil.cpp index 07ab44640..6a1a1376e 100644 --- a/components/sceneutil/lightutil.cpp +++ b/components/sceneutil/lightutil.cpp @@ -58,7 +58,7 @@ namespace SceneUtil light->setQuadraticAttenuation(quadraticAttenuation); } - void addLight(osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior) + osg::ref_ptr addLight(osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior) { SceneUtil::FindByNameVisitor visitor("AttachLight"); node->accept(visitor); @@ -87,6 +87,7 @@ namespace SceneUtil osg::ref_ptr lightSource = createLightSource(esmLight, lightMask, isExterior, osg::Vec4f(0,0,0,1)); attachTo->addChild(lightSource); + return lightSource; } osg::ref_ptr createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient) diff --git a/components/sceneutil/lightutil.hpp b/components/sceneutil/lightutil.hpp index 7096c38b2..1ddfa3d45 100644 --- a/components/sceneutil/lightutil.hpp +++ b/components/sceneutil/lightutil.hpp @@ -32,7 +32,7 @@ namespace SceneUtil /// @param partsysMask Node mask to ignore when computing the sub graph's bounding box. /// @param lightMask Mask to assign to the newly created LightSource. /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use. - void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior); + osg::ref_ptr addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior); /// @brief Convert an ESM::Light to a SceneUtil::LightSource, and return it. /// @param esmLight The light definition coming from the game files containing radius, color, flicker, etc. diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ef5d4a6cb..400cba07f 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -460,91 +460,110 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -