Merge branch 'light_settings_tweak' into 'master'

Lighting patch

Closes #5957 and #5959

See merge request OpenMW/openmw!752
pull/593/head
psi29a 4 years ago
commit d0883f9a0d

@ -111,20 +111,17 @@ namespace
max = MyGUI::utility::parseFloat(widget->getUserString(settingMax)); max = MyGUI::utility::parseFloat(widget->getUserString(settingMax));
} }
const char* getLightingMethodCaptionText(SceneUtil::LightingMethod lightingMethod) void updateMaxLightsComboBox(MyGUI::ComboBox* box)
{ {
switch (lightingMethod) constexpr int min = 8;
{ constexpr int max = 32;
case SceneUtil::LightingMethod::FFP: constexpr int increment = 8;
return "Emulates fixed function pipeline lighting, advanced light settings are disabled when this mode is active"; int maxLights = Settings::Manager::getInt("max lights", "Shaders");
case SceneUtil::LightingMethod::PerObjectUniform: // show increments of 8 in dropdown
return "Removes limit of 8 lights per object, fixes lighting attenuation, and enables groundcover lighting and light fade." if (maxLights >= min && maxLights <= max && !(maxLights % increment))
"\n\nDesigned for compatibility across hardware, and is not meant for large max light counts."; box->setIndexSelected((maxLights / increment)-1);
case SceneUtil::LightingMethod::SingleUBO: else
return "Removes limit of 8 lights per object, fixes lighting attenuation, and enables groundcover lighting and light fade." box->setIndexSelected(MyGUI::ITEM_NONE);
"\n\nDesigned for more modern hardware and large max light counts.";
}
return "";
} }
} }
@ -235,9 +232,9 @@ namespace MWGui
getWidget(mControllerSwitch, "ControllerButton"); getWidget(mControllerSwitch, "ControllerButton");
getWidget(mWaterTextureSize, "WaterTextureSize"); getWidget(mWaterTextureSize, "WaterTextureSize");
getWidget(mWaterReflectionDetail, "WaterReflectionDetail"); getWidget(mWaterReflectionDetail, "WaterReflectionDetail");
getWidget(mLightingMethodText, "LightingMethodText"); getWidget(mLightingMethodButton, "LightingMethodButton");
getWidget(mLightsResetButton, "LightsResetButton"); getWidget(mLightsResetButton, "LightsResetButton");
getWidget(mLightSettingOverlay, "LightSettingOverlay"); getWidget(mMaxLights, "MaxLights");
#ifndef WIN32 #ifndef WIN32
// hide gamma controls since it currently does not work under Linux // hide gamma controls since it currently does not work under Linux
@ -263,7 +260,9 @@ namespace MWGui
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged); mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged); mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);
mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodButtonChanged);
mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked); mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);
mMaxLights->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onMaxLightsChanged);
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked); mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked); mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
@ -310,6 +309,8 @@ namespace MWGui
waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail)); waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail));
mWaterReflectionDetail->setIndexSelected(waterReflectionDetail); mWaterReflectionDetail->setIndexSelected(waterReflectionDetail);
updateMaxLightsComboBox(mMaxLights);
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video")); mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
mKeyboardSwitch->setStateSelected(true); mKeyboardSwitch->setStateSelected(true);
@ -396,19 +397,50 @@ namespace MWGui
apply(); apply();
} }
void SettingsWindow::onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
return;
std::string message = "This change requires a restart to take effect.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, {"#{sOK}"}, true);
Settings::Manager::setString("lighting method", "Shaders", _sender->getItemNameAt(pos));
apply();
}
void SettingsWindow::onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos)
{
int count = 8 * (pos + 1);
Settings::Manager::setInt("max lights", "Shaders", count);
apply();
configureWidgets(mMainWidget, false);
}
void SettingsWindow::onLightsResetButtonClicked(MyGUI::Widget* _sender) void SettingsWindow::onLightsResetButtonClicked(MyGUI::Widget* _sender)
{ {
std::vector<std::string> buttons = {"#{sYes}", "#{sNo}"}; std::vector<std::string> buttons = {"#{sYes}", "#{sNo}"};
std::string message = "Resets to default values, would you like to continue?"; std::string message = "Resets to default values, would you like to continue? Changes to lighting method will require a restart.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons, true); MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons, true);
int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
if (selectedButton == 1 || selectedButton == -1) if (selectedButton == 1 || selectedButton == -1)
return; return;
constexpr std::array<const char*, 5> settings = {"light bounds multiplier", "maximum light distance", "light fade start", "minimum interior brightness", "max lights"}; constexpr std::array<const char*, 6> settings = {
"light bounds multiplier",
"maximum light distance",
"light fade start",
"minimum interior brightness",
"max lights",
"lighting method",
};
for (const auto& setting : settings) for (const auto& setting : settings)
Settings::Manager::setString(setting, "Shaders", Settings::Manager::mDefaultSettings[{"Shaders", setting}]); Settings::Manager::setString(setting, "Shaders", Settings::Manager::mDefaultSettings[{"Shaders", setting}]);
mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(Settings::Manager::mDefaultSettings[{"Shaders", "lighting method"}]));
updateMaxLightsComboBox(mMaxLights);
apply(); apply();
configureWidgets(mMainWidget, false); configureWidgets(mMainWidget, false);
} }
@ -614,20 +646,25 @@ namespace MWGui
void SettingsWindow::updateLightSettings() void SettingsWindow::updateLightSettings()
{ {
auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod(); auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod();
mLightingMethodText->setCaption(SceneUtil::LightManager::getLightingMethodString(lightingMethod)); std::string lightingMethodStr = SceneUtil::LightManager::getLightingMethodString(lightingMethod);
mLightingMethodButton->removeAllItems();
std::array<SceneUtil::LightingMethod, 3> methods = {
SceneUtil::LightingMethod::FFP,
SceneUtil::LightingMethod::PerObjectUniform,
SceneUtil::LightingMethod::SingleUBO,
};
if (lightingMethod == SceneUtil::LightingMethod::FFP || !Settings::Manager::getBool("force shaders", "Shaders")) for (const auto& method : methods)
{ {
MyGUI::Widget* parent = mLightSettingOverlay->getParent(); if (!MWBase::Environment::get().getResourceSystem()->getSceneManager()->isSupportedLightingMethod(method))
mLightSettingOverlay->setEnabled(false); continue;
mLightSettingOverlay->setAlpha(0.8);
parent->setUserString("ToolTipType", "Layout"); mLightingMethodButton->addItem(SceneUtil::LightManager::getLightingMethodString(method));
parent->setUserString("ToolTipLayout", "TextToolTip");
parent->setUserString("Caption_Text", "Unavailable with current settings.");
parent->setEnabled(true);
} }
mLightingMethodText->setUserString("Caption_Text", getLightingMethodCaptionText(lightingMethod)); mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(lightingMethodStr));
} }
void SettingsWindow::layoutControlsBox() void SettingsWindow::layoutControlsBox()

@ -32,8 +32,8 @@ namespace MWGui
MyGUI::ComboBox* mWaterTextureSize; MyGUI::ComboBox* mWaterTextureSize;
MyGUI::ComboBox* mWaterReflectionDetail; MyGUI::ComboBox* mWaterReflectionDetail;
MyGUI::Widget* mLightSettingOverlay; MyGUI::ComboBox* mMaxLights;
MyGUI::TextBox* mLightingMethodText; MyGUI::ComboBox* mLightingMethodButton;
MyGUI::Button* mLightsResetButton; MyGUI::Button* mLightsResetButton;
// controls // controls
@ -56,7 +56,9 @@ namespace MWGui
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos); void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos); void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightsResetButtonClicked(MyGUI::Widget* _sender); void onLightsResetButtonClicked(MyGUI::Widget* _sender);
void onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos);
void onRebindAction(MyGUI::Widget* _sender); void onRebindAction(MyGUI::Widget* _sender);
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel); void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);

@ -233,7 +233,16 @@ namespace MWRender
float positionZ = std::cos(altitude); float positionZ = std::cos(altitude);
light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0)); light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0));
light->setDiffuse(osg::Vec4(diffuseR,diffuseG,diffuseB,1)); light->setDiffuse(osg::Vec4(diffuseR,diffuseG,diffuseB,1));
light->setAmbient(osg::Vec4(ambientR,ambientG,ambientB,1)); osg::Vec4 ambientRGBA = osg::Vec4(ambientR,ambientG,ambientB,1);
if (mResourceSystem->getSceneManager()->getForceShaders())
{
// When using shaders, we now skip the ambient sun calculation as this is the only place it's used.
// Using the scene ambient will give identical results.
lightmodel->setAmbientIntensity(ambientRGBA);
light->setAmbient(osg::Vec4(0,0,0,1));
}
else
light->setAmbient(ambientRGBA);
light->setSpecular(osg::Vec4(0,0,0,0)); light->setSpecular(osg::Vec4(0,0,0,0));
light->setLightNum(0); light->setLightNum(0);
light->setConstantAttenuation(1.f); light->setConstantAttenuation(1.f);

@ -224,8 +224,7 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset); SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
// override sun for local map // override sun for local map
auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod(); SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot.get()), light, stateset);
SceneUtil::configureStateSetSunOverride(lightingMethod, light, stateset);
camera->addChild(lightSource); camera->addChild(lightSource);
camera->setStateSet(stateset); camera->setStateSet(stateset);

@ -201,11 +201,15 @@ namespace MWRender
, mFieldOfViewOverridden(false) , mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f) , mFieldOfViewOverride(0.f)
{ {
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
bool explicitlyForceShaders = Settings::Manager::getBool("force shaders", "Shaders");
// Shadows and radial fog have problems with fixed-function mode // Shadows and radial fog have problems with fixed-function mode
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || explicitlyForceShaders || Settings::Manager::getBool("enable shadows", "Shadows"); bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders")
|| Settings::Manager::getBool("force shaders", "Shaders")
|| Settings::Manager::getBool("enable shadows", "Shadows")
|| lightingMethod != SceneUtil::LightingMethod::FFP;
resourceSystem->getSceneManager()->setForceShaders(forceShaders); resourceSystem->getSceneManager()->setForceShaders(forceShaders);
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped // FIXME: calling dummy method because terrain needs to know whether lighting is clamped
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders")); resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
@ -217,12 +221,11 @@ namespace MWRender
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1); resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1);
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
// Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions. // Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions.
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(!explicitlyForceShaders || lightingMethod == SceneUtil::LightingMethod::FFP); osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(lightingMethod == SceneUtil::LightingMethod::FFP);
resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod()); resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod()); resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setSupportedLightingMethods(sceneRoot->getSupportedLightingMethods());
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); sceneRoot->setLightingMask(Mask_Lighting);
@ -554,7 +557,7 @@ namespace MWRender
// brighten ambient so it reaches the minimum threshold but no more, we want to mess with content data as least we can // brighten ambient so it reaches the minimum threshold but no more, we want to mess with content data as least we can
float targetBrightnessIncreaseFactor = mMinimumAmbientLuminance / relativeLuminance; float targetBrightnessIncreaseFactor = mMinimumAmbientLuminance / relativeLuminance;
if (ambient.r() == 0.f && ambient.g() == 0.f && ambient.b() == 0.f) if (ambient.r() == 0.f && ambient.g() == 0.f && ambient.b() == 0.f)
ambient = osg::Vec4(targetBrightnessIncreaseFactor, targetBrightnessIncreaseFactor, targetBrightnessIncreaseFactor, ambient.a()); ambient = osg::Vec4(mMinimumAmbientLuminance, mMinimumAmbientLuminance, mMinimumAmbientLuminance, ambient.a());
else else
ambient *= targetBrightnessIncreaseFactor; ambient *= targetBrightnessIncreaseFactor;
} }

@ -305,6 +305,16 @@ namespace Resource
mApplyLightingToEnvMaps = apply; mApplyLightingToEnvMaps = apply;
} }
void SceneManager::setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported)
{
mSupportedLightingMethods = supported;
}
bool SceneManager::isSupportedLightingMethod(SceneUtil::LightingMethod method) const
{
return mSupportedLightingMethods[static_cast<int>(method)];
}
void SceneManager::setLightingMethod(SceneUtil::LightingMethod method) void SceneManager::setLightingMethod(SceneUtil::LightingMethod method)
{ {
mLightingMethod = method; mLightingMethod = method;

@ -107,6 +107,9 @@ namespace Resource
void setApplyLightingToEnvMaps(bool apply); void setApplyLightingToEnvMaps(bool apply);
void setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported);
bool isSupportedLightingMethod(SceneUtil::LightingMethod method) const;
void setLightingMethod(SceneUtil::LightingMethod method); void setLightingMethod(SceneUtil::LightingMethod method);
SceneUtil::LightingMethod getLightingMethod() const; SceneUtil::LightingMethod getLightingMethod() const;
@ -197,6 +200,7 @@ namespace Resource
std::string mSpecularMapPattern; std::string mSpecularMapPattern;
bool mApplyLightingToEnvMaps; bool mApplyLightingToEnvMaps;
SceneUtil::LightingMethod mLightingMethod; SceneUtil::LightingMethod mLightingMethod;
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
bool mConvertAlphaTestToAlphaToCoverage; bool mConvertAlphaTestToAlphaToCoverage;
osg::ref_ptr<MultiObjectCache> mInstanceCache; osg::ref_ptr<MultiObjectCache> mInstanceCache;

@ -5,6 +5,8 @@
#include <osg/BufferObject> #include <osg/BufferObject>
#include <osg/BufferIndexBinding> #include <osg/BufferIndexBinding>
#include <osg/Endian> #include <osg/Endian>
#include <osg/Version>
#include <osg/ValueObject>
#include <osgUtil/CullVisitor> #include <osgUtil/CullVisitor>
@ -102,6 +104,7 @@ namespace SceneUtil
, mEndian(osg::getCpuByteOrder()) , mEndian(osg::getCpuByteOrder())
, mCount(count) , mCount(count)
, mStride(12) , mStride(12)
, mCachedSunPos(osg::Vec4())
{ {
mOffsets[Diffuse] = 0; mOffsets[Diffuse] = 0;
mOffsets[Ambient] = 1; mOffsets[Ambient] = 1;
@ -118,6 +121,7 @@ namespace SceneUtil
, mCount(copy.mCount) , mCount(copy.mCount)
, mStride(copy.mStride) , mStride(copy.mStride)
, mOffsets(copy.mOffsets) , mOffsets(copy.mOffsets)
, mCachedSunPos(copy.mCachedSunPos)
{} {}
void setDiffuse(int index, const osg::Vec4& value) void setDiffuse(int index, const osg::Vec4& value)
@ -172,6 +176,17 @@ namespace SceneUtil
return 3 * osg::Vec4::num_components * sizeof(GL_FLOAT) * sz; return 3 * osg::Vec4::num_components * sizeof(GL_FLOAT) * sz;
} }
void setCachedSunPos(const osg::Vec4& pos)
{
mCachedSunPos = pos;
}
void uploadCachedSunPos(const osg::Matrix& viewMat)
{
osg::Vec4 viewPos = mCachedSunPos * viewMat;
std::memcpy(&(*mData)[getOffset(0, Position)], viewPos.ptr(), sizeof(osg::Vec4f));
}
unsigned int asRGBA(const osg::Vec4& value) const unsigned int asRGBA(const osg::Vec4& value) const
{ {
return mEndian == osg::BigEndian ? value.asABGR() : value.asRGBA(); return mEndian == osg::BigEndian ? value.asABGR() : value.asRGBA();
@ -187,6 +202,8 @@ namespace SceneUtil
constexpr auto sizeofFloat = sizeof(GL_FLOAT); constexpr auto sizeofFloat = sizeof(GL_FLOAT);
constexpr auto sizeofVec4 = sizeofFloat * osg::Vec4::num_components; constexpr auto sizeofVec4 = sizeofFloat * osg::Vec4::num_components;
LightBuffer oldBuffer = LightBuffer(*this);
mOffsets[Diffuse] = offsetColors / sizeofFloat; mOffsets[Diffuse] = offsetColors / sizeofFloat;
mOffsets[Ambient] = mOffsets[Diffuse] + 1; mOffsets[Ambient] = mOffsets[Diffuse] + 1;
mOffsets[Specular] = mOffsets[Diffuse] + 2; mOffsets[Specular] = mOffsets[Diffuse] + 2;
@ -196,7 +213,6 @@ namespace SceneUtil
mStride = (offsetAttenuationRadius + sizeofVec4 + stride) / 4; mStride = (offsetAttenuationRadius + sizeofVec4 + stride) / 4;
// Copy over previous buffers light data. Buffers populate before we know the layout. // Copy over previous buffers light data. Buffers populate before we know the layout.
LightBuffer oldBuffer = LightBuffer(*this);
mData->resize(size / sizeofFloat); mData->resize(size / sizeofFloat);
for (int i = 0; i < oldBuffer.mCount; ++i) for (int i = 0; i < oldBuffer.mCount; ++i)
{ {
@ -212,6 +228,7 @@ namespace SceneUtil
int mCount; int mCount;
int mStride; int mStride;
std::array<std::size_t, 6> mOffsets; std::array<std::size_t, 6> mOffsets;
osg::Vec4 mCachedSunPos;
}; };
class LightStateCache class LightStateCache
@ -229,8 +246,9 @@ namespace SceneUtil
return &cacheVector[contextid]; return &cacheVector[contextid];
} }
void configureStateSetSunOverride(LightingMethod method, const osg::Light* light, osg::StateSet* stateset, int mode) void configureStateSetSunOverride(LightManager* lightManager, const osg::Light* light, osg::StateSet* stateset, int mode)
{ {
auto method = lightManager->getLightingMethod();
switch (method) switch (method)
{ {
case LightingMethod::FFP: case LightingMethod::FFP:
@ -244,12 +262,15 @@ namespace SceneUtil
configureAmbient(lightMat, light->getAmbient()); configureAmbient(lightMat, light->getAmbient());
configureDiffuse(lightMat, light->getDiffuse()); configureDiffuse(lightMat, light->getDiffuse());
configureSpecular(lightMat, light->getSpecular()); configureSpecular(lightMat, light->getSpecular());
stateset->addUniform(new osg::Uniform("LightBuffer", lightMat), mode);
osg::ref_ptr<osg::Uniform> uni = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", lightManager->getMaxLights());
uni->setElement(0, lightMat);
stateset->addUniform(uni, mode);
break; break;
} }
case LightingMethod::SingleUBO: case LightingMethod::SingleUBO:
{ {
osg::ref_ptr<LightBuffer> buffer = new LightBuffer(1); osg::ref_ptr<LightBuffer> buffer = new LightBuffer(lightManager->getMaxLightsInScene());
buffer->setDiffuse(0, light->getDiffuse()); buffer->setDiffuse(0, light->getDiffuse());
buffer->setAmbient(0, light->getAmbient()); buffer->setAmbient(0, light->getAmbient());
@ -258,8 +279,11 @@ namespace SceneUtil
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject; osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
buffer->getData()->setBufferObject(ubo); buffer->getData()->setBufferObject(ubo);
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), buffer->getData().get(), 0, buffer->getData()->getTotalDataSize()); #if OSG_VERSION_GREATER_OR_EQUAL(3,5,7)
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), buffer->getData(), 0, buffer->getData()->getTotalDataSize());
#else
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), ubo, 0, buffer->getData()->getTotalDataSize());
#endif
stateset->setAttributeAndModes(ubb, mode); stateset->setAttributeAndModes(ubb, mode);
break; break;
@ -433,6 +457,11 @@ namespace SceneUtil
lightUniform->setElement(i+1, lightMat); lightUniform->setElement(i+1, lightMat);
} }
auto sun = mLightManager->getSunlightBuffer(state.getFrameStamp()->getFrameNumber());
configurePosition(sun, osg::Vec4(sun(0,0), sun(0,1), sun(0,2), 0.0) * state.getInitialViewMatrix());
lightUniform->setElement(0, sun);
lightUniform->dirty(); lightUniform->dirty();
} }
@ -618,7 +647,6 @@ namespace SceneUtil
void operator()(osg::Node* node, osg::NodeVisitor* nv) override void operator()(osg::Node* node, osg::NodeVisitor* nv) override
{ {
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv); osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
bool pop = false;
if (mLastFrameNumber != cv->getTraversalNumber()) if (mLastFrameNumber != cv->getTraversalNumber())
{ {
@ -628,18 +656,24 @@ namespace SceneUtil
{ {
auto stateset = mLightManager->getStateSet(); auto stateset = mLightManager->getStateSet();
auto bo = mLightManager->getLightBuffer(mLastFrameNumber); auto bo = mLightManager->getLightBuffer(mLastFrameNumber);
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData().get(), 0, bo->getData()->getTotalDataSize());
stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON); #if OSG_VERSION_GREATER_OR_EQUAL(3,5,7)
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData(), 0, bo->getData()->getTotalDataSize());
#else
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData()->getBufferObject(), 0, bo->getData()->getTotalDataSize());
#endif
stateset->setAttributeAndModes(ubb, osg::StateAttribute::ON);
} }
auto sun = mLightManager->getSunlight(); auto sun = mLightManager->getSunlight();
if (sun) if (sun)
{ {
// we must defer uploading the transformation to view-space position to deal with different cameras (e.g. reflection RTT).
if (mLightManager->getLightingMethod() == LightingMethod::PerObjectUniform) if (mLightManager->getLightingMethod() == LightingMethod::PerObjectUniform)
{ {
osg::Matrixf lightMat; osg::Matrixf lightMat;
configurePosition(lightMat, sun->getPosition() * (*cv->getCurrentRenderStage()->getInitialViewMatrix())); configurePosition(lightMat, sun->getPosition());
configureAmbient(lightMat, sun->getAmbient()); configureAmbient(lightMat, sun->getAmbient());
configureDiffuse(lightMat, sun->getDiffuse()); configureDiffuse(lightMat, sun->getDiffuse());
configureSpecular(lightMat, sun->getSpecular()); configureSpecular(lightMat, sun->getSpecular());
@ -649,33 +683,15 @@ namespace SceneUtil
{ {
auto buf = mLightManager->getLightBuffer(mLastFrameNumber); auto buf = mLightManager->getLightBuffer(mLastFrameNumber);
buf->setPosition(0, sun->getPosition() * (*cv->getCurrentRenderStage()->getInitialViewMatrix())); buf->setCachedSunPos(sun->getPosition());
buf->setAmbient(0, sun->getAmbient()); buf->setAmbient(0, sun->getAmbient());
buf->setDiffuse(0, sun->getDiffuse()); buf->setDiffuse(0, sun->getDiffuse());
buf->setSpecular(0, sun->getSpecular()); buf->setSpecular(0, sun->getSpecular());
} }
} }
} }
else if (isReflectionCamera(cv->getCurrentCamera()))
{
auto sun = mLightManager->getSunlight();
if (sun)
{
osg::Vec4 originalPos = sun->getPosition();
sun->setPosition(originalPos * (*cv->getCurrentRenderStage()->getInitialViewMatrix()));
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
configureStateSetSunOverride(mLightManager->getLightingMethod(), sun, stateset);
sun->setPosition(originalPos);
cv->pushStateSet(stateset);
pop = true;
}
}
traverse(node, nv); traverse(node, nv);
if (pop)
cv->popStateSet();
} }
private: private:
@ -692,6 +708,7 @@ namespace SceneUtil
LightManagerStateAttribute(LightManager* lightManager) LightManagerStateAttribute(LightManager* lightManager)
: mLightManager(lightManager) : mLightManager(lightManager)
, mDummyProgram(new osg::Program) , mDummyProgram(new osg::Program)
, mInitLayout(false)
{ {
static const std::string dummyVertSource = generateDummyShader(mLightManager->getMaxLightsInScene()); static const std::string dummyVertSource = generateDummyShader(mLightManager->getMaxLightsInScene());
@ -741,8 +758,7 @@ namespace SceneUtil
void apply(osg::State& state) const override void apply(osg::State& state) const override
{ {
static bool init = false; if (!mInitLayout)
if (!init)
{ {
auto handle = mDummyProgram->getPCP(state)->getHandle(); auto handle = mDummyProgram->getPCP(state)->getHandle();
auto* ext = state.get<osg::GLExtensions>(); auto* ext = state.get<osg::GLExtensions>();
@ -754,13 +770,11 @@ namespace SceneUtil
if (activeUniformBlocks > 0) if (activeUniformBlocks > 0)
{ {
initSharedLayout(ext, handle); initSharedLayout(ext, handle);
init = true; mInitLayout = true;
} }
} }
else mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->uploadCachedSunPos(state.getInitialViewMatrix());
{ mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->dirty();
mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->dirty();
}
} }
private: private:
@ -792,36 +806,7 @@ namespace SceneUtil
LightManager* mLightManager; LightManager* mLightManager;
osg::ref_ptr<osg::Program> mDummyProgram; osg::ref_ptr<osg::Program> mDummyProgram;
}; mutable bool mInitLayout;
class LightManagerStateAttributePerObjectUniform : public osg::StateAttribute
{
public:
LightManagerStateAttributePerObjectUniform()
: mLightManager(nullptr) {}
LightManagerStateAttributePerObjectUniform(LightManager* lightManager)
: mLightManager(lightManager)
{
}
LightManagerStateAttributePerObjectUniform(const LightManagerStateAttributePerObjectUniform& copy, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
: osg::StateAttribute(copy,copyop), mLightManager(copy.mLightManager) {}
int compare(const StateAttribute &sa) const override
{
throw std::runtime_error("LightManagerStateAttributePerObjectUniform::compare: unimplemented");
}
META_StateAttribute(NifOsg, LightManagerStateAttributePerObjectUniform, osg::StateAttribute::LIGHT)
void apply(osg::State& state) const override
{
mLightManager->getStateSet()->getUniform("LightBuffer")->setElement(0, mLightManager->getSunlightBuffer(state.getFrameStamp()->getFrameNumber()));
}
private:
LightManager* mLightManager;
}; };
const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = { const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = {
@ -857,6 +842,14 @@ namespace SceneUtil
, mPointLightFadeEnd(0.f) , mPointLightFadeEnd(0.f)
, mPointLightFadeStart(0.f) , mPointLightFadeStart(0.f)
{ {
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
bool supportsGPU4 = exts && exts->isGpuShader4Supported;
mSupported[static_cast<int>(LightingMethod::FFP)] = true;
mSupported[static_cast<int>(LightingMethod::PerObjectUniform)] = true;
mSupported[static_cast<int>(LightingMethod::SingleUBO)] = supportsUBO && supportsGPU4;
setUpdateCallback(new LightManagerUpdateCallback); setUpdateCallback(new LightManagerUpdateCallback);
if (ffp) if (ffp)
@ -870,10 +863,6 @@ namespace SceneUtil
updateSettings(); updateSettings();
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
bool supportsGPU4 = exts && exts->isGpuShader4Supported;
static bool hasLoggedWarnings = false; static bool hasLoggedWarnings = false;
if (lightingMethod == LightingMethod::SingleUBO && !hasLoggedWarnings) if (lightingMethod == LightingMethod::SingleUBO && !hasLoggedWarnings)
@ -1046,7 +1035,8 @@ namespace SceneUtil
setLightingMethod(LightingMethod::PerObjectUniform); setLightingMethod(LightingMethod::PerObjectUniform);
setMaxLights(std::clamp(targetLights, mMaxLightsLowerLimit, mMaxLightsUpperLimit)); setMaxLights(std::clamp(targetLights, mMaxLightsLowerLimit, mMaxLightsUpperLimit));
stateset->setAttributeAndModes(new LightManagerStateAttributePerObjectUniform(this), osg::StateAttribute::ON); // ensures sunlight element in our uniform array is updated when there are no point lights in scene
stateset->setAttributeAndModes(new LightStateAttributePerObjectUniform({}, this), osg::StateAttribute::ON);
stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", getMaxLights())); stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", getMaxLights()));
} }

@ -5,6 +5,7 @@
#include <unordered_set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <memory> #include <memory>
#include <array>
#include <osg/Light> #include <osg/Light>
@ -24,7 +25,7 @@ namespace osgUtil
namespace SceneUtil namespace SceneUtil
{ {
class LightBuffer; class LightBuffer;
class StateSetGenerator; struct StateSetGenerator;
enum class LightingMethod enum class LightingMethod
{ {
@ -33,8 +34,6 @@ namespace SceneUtil
SingleUBO, SingleUBO,
}; };
void configureStateSetSunOverride(LightingMethod method, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
/// LightSource managed by a LightManager. /// LightSource managed by a LightManager.
/// @par Typically used for point lights. Spot lights are not supported yet. Directional lights affect the whole scene /// @par Typically used for point lights. Spot lights are not supported yet. Directional lights affect the whole scene
/// so do not need to be managed by a LightManager - so for directional lights use a plain osg::LightSource instead. /// so do not need to be managed by a LightManager - so for directional lights use a plain osg::LightSource instead.
@ -130,6 +129,7 @@ namespace SceneUtil
}; };
using LightList = std::vector<const LightSourceViewBound*>; using LightList = std::vector<const LightSourceViewBound*>;
using SupportedMethods = std::array<bool, 3>;
META_Node(SceneUtil, LightManager) META_Node(SceneUtil, LightManager)
@ -178,6 +178,8 @@ namespace SceneUtil
osg::Matrixf getSunlightBuffer(size_t frameNum) const { return mSunlightBuffers[frameNum%2]; } osg::Matrixf getSunlightBuffer(size_t frameNum) const { return mSunlightBuffers[frameNum%2]; }
void setSunlightBuffer(const osg::Matrixf& buffer, size_t frameNum) { mSunlightBuffers[frameNum%2] = buffer; } void setSunlightBuffer(const osg::Matrixf& buffer, size_t frameNum) { mSunlightBuffers[frameNum%2] = buffer; }
SupportedMethods getSupportedLightingMethods() { return mSupported; }
std::map<std::string, std::string> getLightDefines() const; std::map<std::string, std::string> getLightDefines() const;
void processChangedSettings(const Settings::CategorySettingVector& changed); void processChangedSettings(const Settings::CategorySettingVector& changed);
@ -232,6 +234,8 @@ namespace SceneUtil
int mMaxLights; int mMaxLights;
SupportedMethods mSupported;
static constexpr auto mMaxLightsLowerLimit = 2; static constexpr auto mMaxLightsLowerLimit = 2;
static constexpr auto mMaxLightsUpperLimit = 64; static constexpr auto mMaxLightsUpperLimit = 64;
static constexpr auto mFFPMaxLights = 8; static constexpr auto mFFPMaxLights = 8;
@ -277,6 +281,8 @@ namespace SceneUtil
std::set<SceneUtil::LightSource*> mIgnoredLightSources; std::set<SceneUtil::LightSource*> mIgnoredLightSources;
}; };
void configureStateSetSunOverride(LightManager* lightManager, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
} }
#endif #endif

@ -174,12 +174,14 @@ this mode when supported and where the GPU is not a bottleneck. On some weaker
devices, using this mode along with :ref:`force per pixel lighting` can carry devices, using this mode along with :ref:`force per pixel lighting` can carry
performance penalties. performance penalties.
Note that when enabled, groundcover lighting is forced to be vertex lighting, When enabled, groundcover lighting is forced to be vertex lighting, unless
unless normal maps are provided. This is due to some groundcover mods using the normal maps are provided. This is due to some groundcover mods using the Z-Up
Z-Up normals technique to avoid some common issues with shading. As a normals technique to avoid some common issues with shading. As a consequence,
consequence, per pixel lighting would give undesirable results. per pixel lighting would give undesirable results.
This setting has no effect if :ref:`force shaders` is 'false'. Note that the rendering will act as if you have 'force shaders' option enabled
when not set to 'legacy'. This means that shaders will be used to render all objects and
the terrain.
light bounds multiplier light bounds multiplier
----------------------- -----------------------

@ -460,109 +460,108 @@
<Widget type="TabItem" skin="" position="4 32 360 308"> <Widget type="TabItem" skin="" position="4 32 360 308">
<Property key="Caption" value=" Lights "/> <Property key="Caption" value=" Lights "/>
<Widget type="Widget" skin="" position="0 0 360 315" align="Stretch"> <!-- Lighting Method -->
<Widget type="Widget" skin="" position="0 0 360 315" align="Stretch" name="LightSettingOverlay"> <Widget type="TextBox" skin="NormalText" position="0 4 170 18" align="Left Top">
<!-- Lighting Method --> <Property key="Caption" value="Lighting Method:"/>
<Widget type="TextBox" skin="NormalText" position="0 4 170 18" align="Left Top"> </Widget>
<Property key="Caption" value="Lighting Method:"/> <Widget type="ComboBox" skin="MW_ComboBox" position="0 28 170 24" align="Left Top" name="LightingMethodButton">
</Widget> <Property key="AddItem" value="legacy"/>
<Widget type="TextBox" skin="SandText" position="0 28 170 18" align="Left Top" name="LightingMethodText"> <Property key="AddItem" value="shaders compatibility"/>
<UserString key="ToolTipType" value="Layout"/> <Property key="AddItem" value="shaders"/>
<UserString key="ToolTipLayout" value="TextToolTip"/> </Widget>
</Widget> <!-- Max Lights -->
<!-- Max Lights --> <Widget type="TextBox" skin="NormalText" position="178 4 160 18" align="Left Top" name="MaxLightsText">
<Widget type="TextBox" skin="NormalText" position="178 4 160 18" align="Left Top" name="MaxLightsText"> <UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipType" value="Layout"/> <UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="ToolTipLayout" value="TextToolTip"/> <UserString key="Caption_Text" value="Default: 8\nMaximum number of lights per object.\n\nA low number near default will cause light popping similar to what you would see with legacy lighting."/>
<UserString key="Caption_Text" value="Default: 8\nMaximum number of lights per object.\n\nA low number near default will cause light popping similar to what you would see with legacy lighting."/> </Widget>
</Widget> <Widget type="ScrollBar" skin="" position="0 0 0 0">
<Widget type="ScrollBar" skin="MW_HScroll" position="178 30 160 18" align="Right Top HStretch" name="MaxLightsSlider"> <UserString key="SettingType" value="Slider"/>
<Property key="Range" value="24"/> <UserString key="SettingCategory" value="Shaders"/>
<Property key="Page" value="1"/> <UserString key="SettingName" value="max lights"/>
<UserString key="SettingType" value="Slider"/> <UserString key="SettingValueType" value="Integer"/>
<UserString key="SettingMin" value="4"/> <UserString key="SettingLabelWidget" value="MaxLightsText"/>
<UserString key="SettingMax" value="32"/> <UserString key="SettingLabelCaption" value="Max Lights: (%s)"/>
<UserString key="SettingCategory" value="Shaders"/> </Widget>
<UserString key="SettingName" value="max lights"/> <Widget type="ComboBox" skin="MW_ComboBox" position="178 28 170 24" align="Left Top" name="MaxLights">
<UserString key="SettingValueType" value="Integer"/> <Property key="AddItem" value="8"/>
<UserString key="SettingLabelWidget" value="MaxLightsText"/> <Property key="AddItem" value="16"/>
<UserString key="SettingLabelCaption" value="Max Lights (%s)"/> <Property key="AddItem" value="24"/>
</Widget> <Property key="AddItem" value="32"/>
<Widget type="ImageBox" skin="MW_HLine" position="0 54 360 18" align="Top HStretch"/> </Widget>
<!-- Light Fade Start --> <Widget type="ImageBox" skin="MW_HLine" position="0 54 360 18" align="Top HStretch"/>
<Widget type="TextBox" skin="NormalText" position="0 78 352 18" align="Left Top" name="MaxLightDistanceText"> <!-- Light Fade Start -->
<UserString key="ToolTipType" value="Layout"/> <Widget type="TextBox" skin="NormalText" position="0 78 352 18" align="Left Top" name="MaxLightDistanceText">
<UserString key="ToolTipLayout" value="TextToolTip"/> <UserString key="ToolTipType" value="Layout"/>
<UserString key="Caption_Text" value="Default: 8192 (units)\nMaximum distance at which lights will appear.\n\nSet this to 0.0 to disable it."/> <UserString key="ToolTipLayout" value="TextToolTip"/>
</Widget> <UserString key="Caption_Text" value="Default: 8192 (units)\nMaximum distance at which lights will appear.\n\nSet this to 0.0 to disable it."/>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 104 352 18" align="HStretch Top"> </Widget>
<Property key="Range" value="8192"/> <Widget type="ScrollBar" skin="MW_HScroll" position="0 104 352 18" align="HStretch Top">
<Property key="Page" value="1"/> <Property key="Range" value="8192"/>
<UserString key="SettingType" value="Slider"/> <Property key="Page" value="1"/>
<UserString key="SettingMin" value="0"/> <UserString key="SettingType" value="Slider"/>
<UserString key="SettingMax" value="8192"/> <UserString key="SettingMin" value="0"/>
<UserString key="SettingCategory" value="Shaders"/> <UserString key="SettingMax" value="8192"/>
<UserString key="SettingName" value="maximum light distance"/> <UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingValueType" value="Integer"/> <UserString key="SettingName" value="maximum light distance"/>
<UserString key="SettingLabelWidget" value="MaxLightDistanceText"/> <UserString key="SettingValueType" value="Integer"/>
<UserString key="SettingLabelCaption" value="Maximum Light Distance (%s)"/> <UserString key="SettingLabelWidget" value="MaxLightDistanceText"/>
</Widget> <UserString key="SettingLabelCaption" value="Maximum Light Distance (%s)"/>
<!-- Light Fade Multiplier --> </Widget>
<Widget type="TextBox" skin="NormalText" position="0 128 352 18" align="Left Top" name="LightFadeMultiplierText"> <!-- Light Fade Multiplier -->
<UserString key="ToolTipType" value="Layout"/> <Widget type="TextBox" skin="NormalText" position="0 128 352 18" align="Left Top" name="LightFadeMultiplierText">
<UserString key="ToolTipLayout" value="TextToolTip"/> <UserString key="ToolTipType" value="Layout"/>
<UserString key="Caption_Text" value="Default: 0.85\nFraction of maximum distance at which lights will start to fade.\n\nSet this to a low value for slower transitions or a high value of quicker transitions."/> <UserString key="ToolTipLayout" value="TextToolTip"/>
</Widget> <UserString key="Caption_Text" value="Default: 0.85\nFraction of maximum distance at which lights will start to fade.\n\nSet this to a low value for slower transitions or a high value for quicker transitions."/>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 152 352 18" align="HStretch Top"> </Widget>
<Property key="Range" value="10000"/> <Widget type="ScrollBar" skin="MW_HScroll" position="0 152 352 18" align="HStretch Top">
<Property key="Page" value="100"/> <Property key="Range" value="10000"/>
<UserString key="SettingType" value="Slider"/> <Property key="Page" value="100"/>
<UserString key="SettingCategory" value="Shaders"/> <UserString key="SettingType" value="Slider"/>
<UserString key="SettingName" value="light fade start"/> <UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingValueType" value="Float"/> <UserString key="SettingName" value="light fade start"/>
<UserString key="SettingMin" value="0.0"/> <UserString key="SettingValueType" value="Float"/>
<UserString key="SettingMax" value="1.0"/> <UserString key="SettingMin" value="0.0"/>
<UserString key="SettingLabelWidget" value="LightFadeMultiplierText"/> <UserString key="SettingMax" value="1.0"/>
<UserString key="SettingLabelCaption" value="Fade Start Multiplier (%s)"/> <UserString key="SettingLabelWidget" value="LightFadeMultiplierText"/>
</Widget> <UserString key="SettingLabelCaption" value="Fade Start Multiplier (%s)"/>
<!-- Bounding Sphere Multiplier --> </Widget>
<Widget type="TextBox" skin="NormalText" position="0 176 352 18" align="Left Top" name="BoundingSphereMultText"> <!-- Bounding Sphere Multiplier -->
<UserString key="ToolTipType" value="Layout"/> <Widget type="TextBox" skin="NormalText" position="0 176 352 18" align="Left Top" name="BoundingSphereMultText">
<UserString key="ToolTipLayout" value="TextToolTip"/> <UserString key="ToolTipType" value="Layout"/>
<UserString key="Caption_Text" value="Default: 1.65\nMultipler for bounding sphere of lights.\nHigher numbers allows for smooth falloff but require an increase in number of max lights.\n\nDoes not effect the illumination or strength of lights."/> <UserString key="ToolTipLayout" value="TextToolTip"/>
</Widget> <UserString key="Caption_Text" value="Default: 1.65\nMultipler for bounding sphere of lights.\nHigher numbers allows for smooth falloff but require an increase in number of max lights.\n\nDoes not effect the illumination or strength of lights."/>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 200 352 18" align="HStretch Top"> </Widget>
<Property key="Range" value="500000"/> <Widget type="ScrollBar" skin="MW_HScroll" position="0 200 352 18" align="HStretch Top">
<Property key="Page" value="1000"/> <Property key="Range" value="500000"/>
<UserString key="SettingType" value="Slider"/> <Property key="Page" value="1000"/>
<UserString key="SettingMin" value="0.0"/> <UserString key="SettingType" value="Slider"/>
<UserString key="SettingMax" value="5.0"/> <UserString key="SettingMin" value="0.0"/>
<UserString key="SettingCategory" value="Shaders"/> <UserString key="SettingMax" value="5.0"/>
<UserString key="SettingName" value="light bounds multiplier"/> <UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingValueType" value="Float"/> <UserString key="SettingName" value="light bounds multiplier"/>
<UserString key="SettingLabelWidget" value="BoundingSphereMultText"/> <UserString key="SettingValueType" value="Float"/>
<UserString key="SettingLabelCaption" value="Bounding Sphere Multiplier (%s)"/> <UserString key="SettingLabelWidget" value="BoundingSphereMultText"/>
</Widget> <UserString key="SettingLabelCaption" value="Bounding Sphere Multiplier (%s)"/>
<!-- Minimum Ambient Brightness --> </Widget>
<Widget type="TextBox" skin="NormalText" position="0 224 352 18" align="Left Top" name="MinimumBrightnessText"> <!-- Minimum Ambient Brightness -->
<UserString key="ToolTipType" value="Layout"/> <Widget type="TextBox" skin="NormalText" position="0 224 352 18" align="Left Top" name="MinimumBrightnessText">
<UserString key="ToolTipLayout" value="TextToolTip"/> <UserString key="ToolTipType" value="Layout"/>
<UserString key="Caption_Text" value="Default: 0.08\nMinimum ambient interior brightness.\n\nIncrease this if you feel interiors are too dark."/> <UserString key="ToolTipLayout" value="TextToolTip"/>
</Widget> <UserString key="Caption_Text" value="Default: 0.08\nMinimum ambient interior brightness.\n\nIncrease this if you feel interiors are too dark."/>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 248 352 18" align="HStretch Top"> </Widget>
<Property key="Range" value="10000"/> <Widget type="ScrollBar" skin="MW_HScroll" position="0 248 352 18" align="HStretch Top">
<Property key="Page" value="100"/> <Property key="Range" value="10000"/>
<UserString key="SettingType" value="Slider"/> <Property key="Page" value="100"/>
<UserString key="SettingCategory" value="Shaders"/> <UserString key="SettingType" value="Slider"/>
<UserString key="SettingName" value="minimum interior brightness"/> <UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingValueType" value="Float"/> <UserString key="SettingName" value="minimum interior brightness"/>
<UserString key="SettingLabelWidget" value="MinimumBrightnessText"/> <UserString key="SettingValueType" value="Float"/>
<UserString key="SettingLabelCaption" value="Minimum Interior Brightness (%s)"/> <UserString key="SettingLabelWidget" value="MinimumBrightnessText"/>
</Widget> <UserString key="SettingLabelCaption" value="Minimum Interior Brightness (%s)"/>
<Widget type="AutoSizedButton" skin="MW_Button" position="0 290 0 0" align="Top Left" name="LightsResetButton">
<Property key="Caption" value="Reset To Defaults"/>
</Widget>
</Widget> </Widget>
<Widget type="AutoSizedButton" skin="MW_Button" position="0 290 0 0" align="Top Left" name="LightsResetButton">
<Property key="Caption" value="Reset To Defaults"/>
</Widget> </Widget>
</Widget> </Widget>
<!-- <!--

Loading…
Cancel
Save