diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 70c496e3f..25ac051f4 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -83,6 +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"] = "0"; for (const auto& define : shadowDefines) defines[define.first] = define.second; mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 61707967b..4d73cde15 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -72,6 +72,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f) mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager; + lightMgr->setStartLight(1); lightMgr->setLightingMask(Mask_Lighting); mRootNode = lightMgr; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d8c759935..7ccf653ea 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1613,7 +1613,7 @@ namespace MWRender { bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); - SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior); + SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior, mResourceSystem->getSceneManager()->getFFPLighting()); } void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 733e1d287..bbd7d0692 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -213,9 +213,11 @@ namespace MWRender resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders")); resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); - resourceSystem->getSceneManager()->setFFPLighting(clampLighting || !forceShaders || !SceneUtil::LightManager::queryNonFFPLightingSupport()); - - osg::ref_ptr sceneRoot = new SceneUtil::LightManager(mResourceSystem->getSceneManager()->getFFPLighting()); + bool useFFP = clampLighting || !forceShaders || !SceneUtil::LightManager::queryNonFFPLightingSupport(); + resourceSystem->getSceneManager()->setFFPLighting(useFFP); + resourceSystem->getSceneManager()->getShaderManager().setFFPLighting(useFFP); + + osg::ref_ptr sceneRoot = new SceneUtil::LightManager(useFFP); sceneRoot->setLightingMask(Mask_Lighting); mSceneRoot = sceneRoot; sceneRoot->setStartLight(1); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 4eac2fe4c..51792d1ba 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -646,7 +646,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R program->addShader(vertexShader); program->addShader(fragmentShader); if (!mResourceSystem->getSceneManager()->getFFPLighting()) - program->addBindUniformBlock("SunlightBuffer", 9); + program->addBindUniformBlock("SunlightBuffer", 0); shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); node->setStateSet(shaderStateset); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 6755db255..ef53bea0d 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -220,7 +220,7 @@ namespace Resource SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) : ResourceManager(vfs) - , mShaderManager(new Shader::ShaderManager(this)) + , mShaderManager(new Shader::ShaderManager) , mForceShaders(false) , mClampLighting(true) , mAutoUseNormalMaps(false) diff --git a/components/sceneutil/lightcontroller.cpp b/components/sceneutil/lightcontroller.cpp index c759fabc7..cc95fe2e4 100644 --- a/components/sceneutil/lightcontroller.cpp +++ b/components/sceneutil/lightcontroller.cpp @@ -11,13 +11,14 @@ namespace SceneUtil { - LightController::LightController() + LightController::LightController(bool useFFPLighting) : mType(LT_Normal) , mPhase(0.25f + Misc::Rng::rollClosedProbability() * 0.75f) , mBrightness(0.675f) , mStartTime(0.0) , mLastTime(0.0) , mTicksToAdvance(0.f) + , mFFPLighting(useFFPLighting) { } @@ -62,7 +63,11 @@ namespace SceneUtil mPhase = mPhase <= 0.5f ? 1.f : 0.25f; } - static_cast(node)->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness); + auto* lightSource = static_cast(node); + if (mFFPLighting) + lightSource->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness); + else + lightSource->setBrightness(nv->getTraversalNumber(), mBrightness); traverse(node, nv); } diff --git a/components/sceneutil/lightcontroller.hpp b/components/sceneutil/lightcontroller.hpp index 36b2e868e..f416be724 100644 --- a/components/sceneutil/lightcontroller.hpp +++ b/components/sceneutil/lightcontroller.hpp @@ -20,7 +20,7 @@ namespace SceneUtil LT_PulseSlow }; - LightController(); + LightController(bool useFFPLighting=true); void setType(LightType type); @@ -36,6 +36,7 @@ namespace SceneUtil double mStartTime; double mLastTime; float mTicksToAdvance; + bool mFFPLighting; }; } diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index fa9dc4fda..987a95e20 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -1,6 +1,7 @@ #include "lightmanager.hpp" #include +#include #include @@ -12,6 +13,8 @@ #include +#include "apps/openmw/mwrender/vismask.hpp" + namespace { /* similar to the boost::hash_combine */ @@ -32,6 +35,10 @@ namespace SceneUtil { static int sLightId = 0; +//////////////////////////////////////////////////////////////////////////////// +// Internal Data Structures +//////////////////////////////////////////////////////////////////////////////// + class LightBuffer : public osg::Referenced { public: @@ -59,11 +66,6 @@ namespace SceneUtil mDirty = false; } - int blockSize() const - { - return mData->getNumElements() * sizeof(GL_FLOAT) * osg::Vec4::num_components; - } - protected: size_t mNumElements; osg::ref_ptr mData; @@ -114,7 +116,7 @@ namespace SceneUtil { public: - enum Type {Diffuse, Ambient, Position, Attenuation}; + enum Type {Position, Diffuse, Ambient, Attenuation}; PointLightBuffer(int count) : LightBuffer(4, count) @@ -124,9 +126,10 @@ namespace SceneUtil { if (getValue(index, type) == value) return; - - (*mData)[mNumElements * index + type] = value; - + + int indexOffset = mNumElements * index + type; + (*mData)[indexOffset] = value; + mDirty = true; } @@ -155,12 +158,16 @@ namespace SceneUtil return &cacheVector[contextid]; } +//////////////////////////////////////////////////////////////////////////////// +// State Attributes +//////////////////////////////////////////////////////////////////////////////// + SunlightStateAttribute::SunlightStateAttribute() : mBuffer(new SunlightBuffer) { osg::ref_ptr ubo = new osg::UniformBufferObject; mBuffer->getData()->setBufferObject(ubo); - mUbb = new osg::UniformBufferBinding(9 , mBuffer->getData().get(), 0, mBuffer->blockSize()); + mUbb = new osg::UniformBufferBinding(0, mBuffer->getData().get(), 0, mBuffer->getData()->getTotalDataSize()); } SunlightStateAttribute::SunlightStateAttribute(const SunlightStateAttribute ©, const osg::CopyOp ©op) @@ -235,56 +242,6 @@ namespace SceneUtil osg::Vec4f mnullptr; }; - class LightStateAttribute : public osg::StateAttribute - { - public: - LightStateAttribute() : mBuffer(nullptr) {} - LightStateAttribute(const std::vector >& lights, const osg::ref_ptr& buffer) : mLights(lights), mBuffer(buffer) {} - - LightStateAttribute(const LightStateAttribute& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) - : osg::StateAttribute(copy,copyop), mLights(copy.mLights), mBuffer(copy.mBuffer) {} - - int compare(const StateAttribute &sa) const override - { - throw std::runtime_error("LightStateAttribute::compare: unimplemented"); - } - - META_StateAttribute(NifOsg, LightStateAttribute, osg::StateAttribute::LIGHT) - - void apply(osg::State& state) const override - { - if (mLights.empty()) - return; - osg::Matrix modelViewMatrix = state.getModelViewMatrix(); - - state.applyModelViewMatrix(state.getInitialViewMatrix()); - - if (mBuffer) - { - for (size_t i = 0; i < mLights.size(); ++i) - { - - mBuffer->setValue(i, PointLightBuffer::Diffuse, mLights[i]->getDiffuse()); - mBuffer->setValue(i, PointLightBuffer::Ambient, mLights[i]->getAmbient()); - mBuffer->setValue(i, PointLightBuffer::Position, mLights[i]->getPosition() * state.getModelViewMatrix()); - mBuffer->setValue(i, PointLightBuffer::Attenuation, - osg::Vec4(mLights[i]->getConstantAttenuation(), - mLights[i]->getLinearAttenuation(), - mLights[i]->getQuadraticAttenuation(), 0.0)); - } - - if (mBuffer && mBuffer->isDirty()) - mBuffer->dirty(); - } - - state.applyModelViewMatrix(modelViewMatrix); - } - - private: - std::vector> mLights; - osg::ref_ptr mBuffer; - }; - class FFPLightStateAttribute : public osg::StateAttribute { public: @@ -368,6 +325,10 @@ namespace SceneUtil return nullptr; } +//////////////////////////////////////////////////////////////////////////////// +// Node Callbacks +//////////////////////////////////////////////////////////////////////////////// + // Set on a LightSource. Adds the light source to its light manager for the current frame. // This allows us to keep track of the current lights in the scene graph without tying creation & destruction to the manager. class CollectLightCallback : public osg::NodeCallback @@ -460,6 +421,42 @@ namespace SceneUtil size_t mLastFrameNumber; }; + class LightManagerStateAttribute : public osg::StateAttribute + { + public: + LightManagerStateAttribute() : mLightManager(nullptr) {} + LightManagerStateAttribute(LightManager* lightManager) : mLightManager(lightManager) {} + + LightManagerStateAttribute(const LightManagerStateAttribute& 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("LightManagerStateAttribute::compare: unimplemented"); + } + + META_StateAttribute(NifOsg, LightManagerStateAttribute, osg::StateAttribute::LIGHT) + + void apply(osg::State& state) const override + { + osg::Matrix modelViewMatrix = state.getModelViewMatrix(); + + state.applyModelViewMatrix(state.getInitialViewMatrix()); + + for (size_t i = 0; i < mLightManager->mPointLightProxyData.size(); i++) + { + auto& data = mLightManager->mPointLightProxyData[i]; + auto pos = data.mPosition * state.getInitialViewMatrix(); + pos[3] = data.mBrightness; + mLightManager->mPointBuffer->setValue(i, PointLightBuffer::Position, pos); + } + state.applyModelViewMatrix(modelViewMatrix); + mLightManager->mPointBuffer->dirty(); + } + + LightManager* mLightManager; + }; + LightManager::LightManager(bool ffp) : mStartLight(0) , mLightingMask(~0u) @@ -467,28 +464,44 @@ namespace SceneUtil , mSunBuffer(nullptr) , mFFP(ffp) { - setUpdateCallback(new LightManagerUpdateCallback); - if (usingFFP()) { for (int i=0; i >())); + setUpdateCallback(new LightManagerUpdateCallback); return; } auto* stateset = getOrCreateStateSet(); - mSunBuffer = new SunlightBuffer(); - osg::ref_ptr ubo = new osg::UniformBufferObject; - mSunBuffer->getData()->setBufferObject(ubo); - osg::ref_ptr ubb = new osg::UniformBufferBinding(9 , mSunBuffer->getData().get(), 0, mSunBuffer->blockSize()); - stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON); + { // sunlight UBO data + mSunBuffer = new SunlightBuffer(); + osg::ref_ptr ubo = new osg::UniformBufferObject; + mSunBuffer->getData()->setBufferObject(ubo); + osg::ref_ptr ubb = new osg::UniformBufferBinding(0, mSunBuffer->getData().get(), 0, mSunBuffer->getData()->getTotalDataSize()); + stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON); + } + { // point lights UBO data + mPointBuffer = new PointLightBuffer(getMaxLightsInScene()); + osg::ref_ptr ubo = new osg::UniformBufferObject; + mPointBuffer->getData()->setBufferObject(ubo); + osg::ref_ptr ubb = new osg::UniformBufferBinding(1, mPointBuffer->getData().get(), 0, mPointBuffer->getData()->getTotalDataSize()); + stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON); + } + + mPointLightProxyData.resize(getMaxLightsInScene()); + + stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); + stateset->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("PointLightCount", 0)); + setUpdateCallback(new LightManagerUpdateCallback); addCullCallback(new SunlightCallback(this)); } +//////////////////////////////////////////////////////////////////////////////// + LightManager::LightManager(const LightManager ©, const osg::CopyOp ©op) : osg::Group(copy, copyop) , mStartLight(copy.mStartLight) @@ -507,7 +520,7 @@ namespace SceneUtil int LightManager::getMaxLights() const { if (usingFFP()) return LightManager::mFFPMaxLights; - return std::clamp(Settings::Manager::getInt("max lights", "Shaders"), 0, getMaxLightsInScene()); + return std::clamp(Settings::Manager::getInt("max lights", "Shaders"),LightManager::mFFPMaxLights , getMaxLightsInScene()); } int LightManager::getMaxLightsInScene() const @@ -579,16 +592,18 @@ namespace SceneUtil } void LightManager::update() - { + { mLights.clear(); mLightsInViewSpace.clear(); - // do an occasional cleanup for orphaned lights + // Do an occasional cleanup for orphaned lights. for (int i=0; i<2; ++i) { - if (mStateSetCache[i].size() > 5000) + if (mStateSetCache[i].size() > 5000 || mIndexNeedsRecompiling) mStateSetCache[i].clear(); } + + mIndexNeedsRecompiling = false; } void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum) @@ -599,7 +614,8 @@ namespace SceneUtil lightSource->getLight(frameNum)->setPosition(osg::Vec4f(worldMat.getTrans().x(), worldMat.getTrans().y(), worldMat.getTrans().z(), 1.f)); - mLights.push_back(l); + if (mLights.size() < static_cast(getMaxLightsInScene())) + mLights.emplace_back(l); } void LightManager::setSunlight(osg::ref_ptr sun) @@ -627,20 +643,21 @@ namespace SceneUtil hash_combine(hash, lightList[i]->mLightSource->getId()); LightStateSetMap& stateSetCache = mStateSetCache[frameNum%2]; - + LightStateSetMap::iterator found = stateSetCache.find(hash); if (found != stateSetCache.end()) return found->second; else { osg::ref_ptr stateset = new osg::StateSet; - std::vector > lights; - lights.reserve(lightList.size()); - for (size_t i=0; imLightSource->getLight(frameNum)); if (usingFFP()) { + std::vector > lights; + lights.reserve(lightList.size()); + for (size_t i=0; imLightSource->getLight(frameNum)); + // the first light state attribute handles the actual state setting for all lights // it's best to batch these up so that we don't need to touch the modelView matrix more than necessary // don't use setAttributeAndModes, that does not support light indices! @@ -656,14 +673,18 @@ namespace SceneUtil } else { - osg::ref_ptr buffer = new PointLightBuffer(lights.size()); - osg::ref_ptr ubo = new osg::UniformBufferObject; - buffer->getData()->setBufferObject(ubo); - osg::ref_ptr ubb = new osg::UniformBufferBinding(8 ,buffer->getData().get(), 0, buffer->blockSize()); - stateset->addUniform(new osg::Uniform("PointLightCount", (int)lights.size())); - stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON); - - stateset->setAttribute(new LightStateAttribute(std::move(lights), buffer), osg::StateAttribute::ON); + osg::ref_ptr indices = new osg::IntArray(getMaxLights()); + osg::ref_ptr indicesUni = new osg::Uniform(osg::Uniform::Type::INT, "PointLightIndex", indices->size()); + int validCount = 0; + for (size_t i = 0; i < lightList.size(); ++i) + { + auto it = mLightData.find(lightList[i]->mLightSource->getId()); + if (it != mLightData.end()) + indices->at(validCount++) = it->second; + } + indicesUni->setArray(indices); + stateset->addUniform(indicesUni); + stateset->addUniform(new osg::Uniform("PointLightCount", validCount)); } stateSetCache.emplace(hash, stateset); @@ -671,11 +692,6 @@ namespace SceneUtil } } - const std::vector& LightManager::getLights() const - { - return mLights; - } - const std::vector& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum) { osg::observer_ptr camPtr (camera); @@ -695,13 +711,47 @@ namespace SceneUtil l.mLightSource = lightIt->mLightSource; l.mViewBound = viewBound; it->second.push_back(l); + + if (usingFFP()) continue; + + auto* light = l.mLightSource->getLight(frameNum); + + auto dataIt = mLightData.find(l.mLightSource->getId()); + if (dataIt != mLightData.end()) + { + mPointLightProxyData[dataIt->second].mPosition = light->getPosition(); + mPointLightProxyData[dataIt->second].mBrightness = l.mLightSource->getBrightness(frameNum); + mPointBuffer->setValue(dataIt->second, PointLightBuffer::Diffuse, light->getDiffuse()); + continue; + } + + if (mLightData.size() >= static_cast(getMaxLightsInScene())) + { + mIndexNeedsRecompiling = true; + mLightData.clear(); + } + + int index = mLightData.size(); + updateGPUPointLight(index, l.mLightSource, frameNum); + mLightData.emplace(l.mLightSource->getId(), index); } } return it->second; } + void LightManager::updateGPUPointLight(int index, LightSource* lightSource, size_t frameNum) + { + auto* light = lightSource->getLight(frameNum); + mPointLightProxyData[index].mPosition = light->getPosition(); + mPointLightProxyData[index].mBrightness = lightSource->getBrightness(frameNum); + mPointBuffer->setValue(index, PointLightBuffer::Diffuse, light->getDiffuse()); + mPointBuffer->setValue(index, PointLightBuffer::Ambient, light->getAmbient()); + mPointBuffer->setValue(index, PointLightBuffer::Attenuation, osg::Vec4(light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), lightSource->getRadius())); + } + LightSource::LightSource() : mRadius(0.f) + , mBrightness{1.0,1.0} { setUpdateCallback(new CollectLightCallback); mId = sLightId++; @@ -753,7 +803,7 @@ namespace SceneUtil // Don't use Camera::getViewMatrix, that one might be relative to another camera! const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix(); const std::vector& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix, mLastFrameNumber); - + // get the node bounds in view space // NB do not node->getBound() * modelView, that would apply the node's transformation twice osg::BoundingSphere nodeBound; @@ -818,7 +868,6 @@ namespace SceneUtil else stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber()); - cv->pushStateSet(stateset); return true; } diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index 7ceb725ae..9c478b294 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -2,13 +2,15 @@ #define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H #include +#include +#include +#include #include #include #include #include -#include #include @@ -17,9 +19,16 @@ namespace osgUtil class CullVisitor; } +namespace osg +{ + class UniformBufferBinding; + class UniformBufferObject; +} + namespace SceneUtil { class SunlightBuffer; + class PointLightBuffer; // Used to override sun. Rarely useful but necassary for local map. class SunlightStateAttribute : public osg::StateAttribute @@ -59,6 +68,8 @@ namespace SceneUtil int mId; + float mBrightness[2]; + public: META_Node(SceneUtil, LightSource) @@ -78,6 +89,16 @@ namespace SceneUtil mRadius = radius; } + float getBrightness(size_t frame) + { + return mBrightness[frame % 2]; + } + + void setBrightness(size_t frame, float brightness) + { + mBrightness[frame % 2] = brightness; + } + /// 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. @@ -119,7 +140,7 @@ namespace SceneUtil osg::BoundingSphere mViewBound; }; - typedef std::vector LightList; + using LightList = std::vector; static bool queryNonFFPLightingSupport(); @@ -146,8 +167,6 @@ namespace SceneUtil /// Internal use only, called automatically by the LightSource's UpdateCallback void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum); - const std::vector& getLights() const; - const std::vector& getLightsInViewSpace(osg::Camera* camera, const osg::RefMatrix* viewMatrix, size_t frameNum); osg::ref_ptr getLightListStateSet(const LightList& lightList, size_t frameNum); @@ -165,6 +184,11 @@ namespace SceneUtil Shader::ShaderManager::DefineMap getLightDefines() const; private: + + friend class LightManagerStateAttribute; + + void updateGPUPointLight(int index, LightSource* lightSource, size_t frameNum); + // Lights collected from the scene graph. Only valid during the cull traversal. std::vector mLights; @@ -184,6 +208,21 @@ namespace SceneUtil osg::ref_ptr mSun; osg::ref_ptr mSunBuffer; + struct PointLightProxyData + { + osg::Vec4 mPosition; + float mBrightness; + }; + + std::vector mPointLightProxyData; + osg::ref_ptr mPointBuffer; + + // < Light ID , Buffer Index > + using LightDataMap = std::unordered_map; + LightDataMap mLightData; + + bool mIndexNeedsRecompiling; + bool mFFP; static constexpr int mFFPMaxLights = 8; diff --git a/components/sceneutil/lightutil.cpp b/components/sceneutil/lightutil.cpp index e9be05908..6c17117d0 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) + void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior, bool useFFPLighting) { SceneUtil::FindByNameVisitor visitor("AttachLight"); node->accept(visitor); @@ -85,17 +85,21 @@ namespace SceneUtil attachTo = trans; } - osg::ref_ptr lightSource = createLightSource(esmLight, lightMask, isExterior); + osg::ref_ptr lightSource = createLightSource(esmLight, lightMask, isExterior, osg::Vec4f(0,0,0,1), useFFPLighting); attachTo->addChild(lightSource); } - osg::ref_ptr createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient) + osg::ref_ptr createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient, bool useFFPLighting) { osg::ref_ptr lightSource (new SceneUtil::LightSource); osg::ref_ptr light (new osg::Light); lightSource->setNodeMask(lightMask); float radius = esmLight->mData.mRadius; + // arbitrary multipler to reduce light popping, this is hard to avoid with per-object lighting + // we offset this multipler in shaders + if (!useFFPLighting) + radius *= 2.0; lightSource->setRadius(radius); configureLight(light, radius, isExterior); @@ -112,7 +116,7 @@ namespace SceneUtil lightSource->setLight(light); - osg::ref_ptr ctrl (new SceneUtil::LightController); + osg::ref_ptr ctrl (new SceneUtil::LightController(useFFPLighting)); ctrl->setDiffuse(light->getDiffuse()); if (esmLight->mData.mFlags & ESM::Light::Flicker) ctrl->setType(SceneUtil::LightController::LT_Flicker); diff --git a/components/sceneutil/lightutil.hpp b/components/sceneutil/lightutil.hpp index 7096c38b2..bb28276e9 100644 --- a/components/sceneutil/lightutil.hpp +++ b/components/sceneutil/lightutil.hpp @@ -32,14 +32,14 @@ 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); + void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior, bool useFFPLighting=true); /// @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. /// @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. /// @param ambient Ambient component of the light. - osg::ref_ptr createLightSource (const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1)); + osg::ref_ptr createLightSource (const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1), bool useFFPLighting=true); } diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 96671bf8b..b1dc145f7 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -17,10 +17,9 @@ namespace Shader { - ShaderManager::ShaderManager(Resource::SceneManager* sceneManager) - : mSceneManager(sceneManager) + ShaderManager::ShaderManager() + : mFFPLighting(false) { - } void ShaderManager::setShaderPath(const std::string &path) @@ -28,6 +27,11 @@ namespace Shader mPath = path; } + void ShaderManager::setFFPLighting(bool useFFP) + { + mFFPLighting = useFFP; + } + bool addLineDirectivesAfterConditionalBlocks(std::string& source) { for (size_t position = 0; position < source.length(); ) @@ -352,10 +356,10 @@ namespace Shader program->addShader(fragmentShader); program->addBindAttribLocation("aOffset", 6); program->addBindAttribLocation("aRotation", 7); - if (!mSceneManager->getFFPLighting()) + if (!mFFPLighting) { - program->addBindUniformBlock("PointLightBuffer", 8); - program->addBindUniformBlock("SunlightBuffer", 9); + program->addBindUniformBlock("SunlightBuffer", 0); + program->addBindUniformBlock("PointLightBuffer", 1); } found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first; } diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index b9bb005b1..c70085485 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -25,10 +25,12 @@ namespace Shader { public: - ShaderManager(Resource::SceneManager* sceneManager); + ShaderManager(); void setShaderPath(const std::string& path); + void setFFPLighting(bool useFFP); + typedef std::map DefineMap; /// Create or retrieve a shader instance. @@ -67,7 +69,7 @@ namespace Shader typedef std::map, osg::ref_ptr >, osg::ref_ptr > ProgramMap; ProgramMap mPrograms; - Resource::SceneManager* mSceneManager; + bool mFFPLighting; std::mutex mMutex; }; diff --git a/files/shaders/lighting.glsl b/files/shaders/lighting.glsl index b9f426fc9..b243a90c4 100644 --- a/files/shaders/lighting.glsl +++ b/files/shaders/lighting.glsl @@ -6,19 +6,21 @@ struct PointLight { + vec4 position; vec4 diffuse; vec4 ambient; - vec4 position; - vec4 attenuation; + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + float radius; }; -uniform mat4 osg_ViewMatrix; uniform int PointLightCount; uniform int PointLightIndex[@maxLights]; layout(std140) uniform PointLightBuffer { - PointLight PointLights[@maxLights]; + PointLight PointLights[@maxLightsInScene]; }; #else @@ -47,15 +49,26 @@ void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 vi diffuseOut = @sunDiffuse.xyz * lambert; } + +uniform float osg_SimulationTime; void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal) { - vec3 lightDir = getLight[lightIndex].position.xyz - viewPos; - //vec3 lightDir = (osg_ViewMatrix * vec4(getLight[lightIndex].position, 1.0)).xyz - viewPos; + vec4 pos = getLight[lightIndex].position; + vec3 lightDir = pos.xyz - viewPos; float lightDistance = length(lightDir); lightDir = normalize(lightDir); - float illumination = clamp(1.0 / (getLight[lightIndex].attenuation.x + getLight[lightIndex].attenuation.y * lightDistance + getLight[lightIndex].attenuation.z * lightDistance * lightDistance), 0.0, 1.0); + float illumination = clamp(1.0 / (getLight[lightIndex].constantAttenuation + getLight[lightIndex].linearAttenuation * lightDistance + getLight[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0); + +// Add an artificial cutoff, otherwise effected objects will be brightly lit and adjacent objects not effected by this light will be dark by contrast +// This causes nasty artifacts, especially with active grid so it is necassary for now. +#if !@ffpLighting + float cutoff = getLight[lightIndex].radius * 0.5; + illumination *= 1.0 - smoothstep(0.0, 1.0, ((lightDistance / cutoff) - 1.0) * 0.887); + illumination = max(0.0, illumination); +#endif + ambientOut = getLight[lightIndex].ambient.xyz * illumination; float lambert = dot(viewNormal.xyz, lightDir) * illumination; @@ -70,7 +83,12 @@ void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec } lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0); #endif + +#if @ffpLighting diffuseOut = getLight[lightIndex].diffuse.xyz * lambert; +#else + diffuseOut = (getLight[lightIndex].diffuse.xyz * pos.w) * lambert; +#endif } #if PER_PIXEL_LIGHTING @@ -97,7 +115,7 @@ void doLighting(vec3 viewPos, vec3 viewNormal, out vec3 diffuseLight, out vec3 a diffuseLight += diffuseOut; for (int i=0; i