|
|
|
@ -1,11 +1,145 @@
|
|
|
|
|
#include "lightmanager.hpp"
|
|
|
|
|
|
|
|
|
|
#include <osg/BufferObject>
|
|
|
|
|
|
|
|
|
|
#include <osgUtil/CullVisitor>
|
|
|
|
|
|
|
|
|
|
#include <components/sceneutil/util.hpp>
|
|
|
|
|
|
|
|
|
|
#include <components/misc/stringops.hpp>
|
|
|
|
|
|
|
|
|
|
#include <components/settings/settings.hpp>
|
|
|
|
|
|
|
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
/* similar to the boost::hash_combine */
|
|
|
|
|
template <class T>
|
|
|
|
|
inline void hash_combine(std::size_t& seed, const T& v)
|
|
|
|
|
{
|
|
|
|
|
std::hash<T> hasher;
|
|
|
|
|
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool sortLights(const SceneUtil::LightManager::LightSourceViewBound* left, const SceneUtil::LightManager::LightSourceViewBound* right)
|
|
|
|
|
{
|
|
|
|
|
return left->mViewBound.center().length2() - left->mViewBound.radius2()*81 < right->mViewBound.center().length2() - right->mViewBound.radius2()*81;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace SceneUtil
|
|
|
|
|
{
|
|
|
|
|
static int sLightId = 0;
|
|
|
|
|
|
|
|
|
|
class LightBuffer : public osg::Referenced
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
LightBuffer(int elements, int count=1)
|
|
|
|
|
: mNumElements(elements)
|
|
|
|
|
, mData(new osg::Vec4Array(elements * count))
|
|
|
|
|
, mDirty(false)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
virtual ~LightBuffer() {}
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Vec4Array> getData()
|
|
|
|
|
{
|
|
|
|
|
return mData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isDirty() const
|
|
|
|
|
{
|
|
|
|
|
return mDirty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dirty()
|
|
|
|
|
{
|
|
|
|
|
mData->dirty();
|
|
|
|
|
mDirty = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int blockSize() const
|
|
|
|
|
{
|
|
|
|
|
return mData->getNumElements() * sizeof(GL_FLOAT) * osg::Vec4::num_components;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
size_t mNumElements;
|
|
|
|
|
osg::ref_ptr<osg::Vec4Array> mData;
|
|
|
|
|
bool mDirty;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* struct:
|
|
|
|
|
* vec4 diffuse
|
|
|
|
|
* vec4 ambient
|
|
|
|
|
* vec4 specular
|
|
|
|
|
* vec4 direction
|
|
|
|
|
*/
|
|
|
|
|
class SunlightBuffer : public LightBuffer
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
enum Type {Diffuse, Ambient, Specular, Direction};
|
|
|
|
|
|
|
|
|
|
SunlightBuffer()
|
|
|
|
|
: LightBuffer(4)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void setValue(Type type, const osg::Vec4& value)
|
|
|
|
|
{
|
|
|
|
|
if (getValue(type) == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
(*mData)[type] = value;
|
|
|
|
|
|
|
|
|
|
mDirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::Vec4 getValue(Type type)
|
|
|
|
|
{
|
|
|
|
|
return (*mData)[type];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* struct:
|
|
|
|
|
* vec4 diffuse
|
|
|
|
|
* vec4 ambient
|
|
|
|
|
* vec4 position
|
|
|
|
|
* vec4 illumination (constant, linear, quadratic)
|
|
|
|
|
*/
|
|
|
|
|
class PointLightBuffer : public LightBuffer
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
enum Type {Diffuse, Ambient, Position, Attenuation};
|
|
|
|
|
|
|
|
|
|
PointLightBuffer(int count)
|
|
|
|
|
: LightBuffer(4, count)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void setValue(int index, Type type, const osg::Vec4& value)
|
|
|
|
|
{
|
|
|
|
|
if (getValue(index, type) == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
(*mData)[mNumElements * index + type] = value;
|
|
|
|
|
|
|
|
|
|
mDirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::Vec4 getValue(int index, Type type)
|
|
|
|
|
{
|
|
|
|
|
return (*mData)[mNumElements * index + type];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static constexpr int queryBlockSize(int sz)
|
|
|
|
|
{
|
|
|
|
|
return 4 * osg::Vec4::num_components * sizeof(GL_FLOAT) * sz;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class LightStateCache
|
|
|
|
|
{
|
|
|
|
@ -13,7 +147,7 @@ namespace SceneUtil
|
|
|
|
|
osg::Light* lastAppliedLight[8];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LightStateCache* getLightStateCache(unsigned int contextid)
|
|
|
|
|
LightStateCache* getLightStateCache(size_t contextid)
|
|
|
|
|
{
|
|
|
|
|
static std::vector<LightStateCache> cacheVector;
|
|
|
|
|
if (cacheVector.size() < contextid+1)
|
|
|
|
@ -21,14 +155,143 @@ namespace SceneUtil
|
|
|
|
|
return &cacheVector[contextid];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resets the modelview matrix to just the view matrix before applying lights.
|
|
|
|
|
SunlightStateAttribute::SunlightStateAttribute()
|
|
|
|
|
: mBuffer(new SunlightBuffer)
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
|
|
|
|
|
mBuffer->getData()->setBufferObject(ubo);
|
|
|
|
|
mUbb = new osg::UniformBufferBinding(9 , mBuffer->getData().get(), 0, mBuffer->blockSize());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SunlightStateAttribute::SunlightStateAttribute(const SunlightStateAttribute ©, const osg::CopyOp ©op)
|
|
|
|
|
: osg::StateAttribute(copy, copyop), mBuffer(copy.mBuffer)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
int SunlightStateAttribute::compare(const StateAttribute &sa) const
|
|
|
|
|
{
|
|
|
|
|
throw std::runtime_error("LightStateAttribute::compare: unimplemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SunlightStateAttribute::setFromLight(const osg::Light* light)
|
|
|
|
|
{
|
|
|
|
|
mBuffer->setValue(SunlightBuffer::Diffuse, light->getDiffuse());
|
|
|
|
|
mBuffer->setValue(SunlightBuffer::Ambient, light->getAmbient());
|
|
|
|
|
mBuffer->setValue(SunlightBuffer::Specular, light->getSpecular());
|
|
|
|
|
mBuffer->setValue(SunlightBuffer::Direction, light->getPosition());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SunlightStateAttribute::setStateSet(osg::StateSet* stateset, int mode)
|
|
|
|
|
{
|
|
|
|
|
stateset->setAttributeAndModes(mUbb, mode);
|
|
|
|
|
stateset->setAssociatedModes(this, mode);
|
|
|
|
|
mBuffer->dirty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class DisableLight : public osg::StateAttribute
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DisableLight() : mIndex(0) {}
|
|
|
|
|
DisableLight(int index) : mIndex(index) {}
|
|
|
|
|
|
|
|
|
|
DisableLight(const DisableLight& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
|
|
|
|
: osg::StateAttribute(copy,copyop), mIndex(copy.mIndex) {}
|
|
|
|
|
|
|
|
|
|
osg::Object* cloneType() const override { return new DisableLight(mIndex); }
|
|
|
|
|
osg::Object* clone(const osg::CopyOp& copyop) const override { return new DisableLight(*this,copyop); }
|
|
|
|
|
bool isSameKindAs(const osg::Object* obj) const override { return dynamic_cast<const DisableLight *>(obj)!=nullptr; }
|
|
|
|
|
const char* libraryName() const override { return "SceneUtil"; }
|
|
|
|
|
const char* className() const override { return "DisableLight"; }
|
|
|
|
|
Type getType() const override { return LIGHT; }
|
|
|
|
|
|
|
|
|
|
unsigned int getMember() const override
|
|
|
|
|
{
|
|
|
|
|
return mIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool getModeUsage(ModeUsage & usage) const override
|
|
|
|
|
{
|
|
|
|
|
usage.usesMode(GL_LIGHT0 + mIndex);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int compare(const StateAttribute &sa) const override
|
|
|
|
|
{
|
|
|
|
|
throw std::runtime_error("DisableLight::compare: unimplemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void apply(osg::State& state) const override
|
|
|
|
|
{
|
|
|
|
|
int lightNum = GL_LIGHT0 + mIndex;
|
|
|
|
|
glLightfv( lightNum, GL_AMBIENT, mnullptr.ptr() );
|
|
|
|
|
glLightfv( lightNum, GL_DIFFUSE, mnullptr.ptr() );
|
|
|
|
|
glLightfv( lightNum, GL_SPECULAR, mnullptr.ptr() );
|
|
|
|
|
|
|
|
|
|
LightStateCache* cache = getLightStateCache(state.getContextID());
|
|
|
|
|
cache->lastAppliedLight[mIndex] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
size_t mIndex;
|
|
|
|
|
osg::Vec4f mnullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class LightStateAttribute : public osg::StateAttribute
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
LightStateAttribute() : mIndex(0) {}
|
|
|
|
|
LightStateAttribute(unsigned int index, const std::vector<osg::ref_ptr<osg::Light> >& lights) : mIndex(index), mLights(lights) {}
|
|
|
|
|
LightStateAttribute() : mBuffer(nullptr) {}
|
|
|
|
|
LightStateAttribute(const std::vector<osg::ref_ptr<osg::Light> >& lights, const osg::ref_ptr<PointLightBuffer>& 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<osg::ref_ptr<osg::Light>> mLights;
|
|
|
|
|
osg::ref_ptr<PointLightBuffer> mBuffer;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class FFPLightStateAttribute : public osg::StateAttribute
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FFPLightStateAttribute() : mIndex(0) {}
|
|
|
|
|
FFPLightStateAttribute(size_t index, const std::vector<osg::ref_ptr<osg::Light> >& lights) : mIndex(index), mLights(lights) {}
|
|
|
|
|
|
|
|
|
|
FFPLightStateAttribute(const FFPLightStateAttribute& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
|
|
|
|
: osg::StateAttribute(copy,copyop), mIndex(copy.mIndex), mLights(copy.mLights) {}
|
|
|
|
|
|
|
|
|
|
unsigned int getMember() const override
|
|
|
|
@ -38,17 +301,17 @@ namespace SceneUtil
|
|
|
|
|
|
|
|
|
|
bool getModeUsage(ModeUsage & usage) const override
|
|
|
|
|
{
|
|
|
|
|
for (unsigned int i=0; i<mLights.size(); ++i)
|
|
|
|
|
for (size_t i=0; i<mLights.size(); ++i)
|
|
|
|
|
usage.usesMode(GL_LIGHT0 + mIndex + i);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int compare(const StateAttribute &sa) const override
|
|
|
|
|
{
|
|
|
|
|
throw std::runtime_error("LightStateAttribute::compare: unimplemented");
|
|
|
|
|
throw std::runtime_error("FFPLightStateAttribute::compare: unimplemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
META_StateAttribute(NifOsg, LightStateAttribute, osg::StateAttribute::LIGHT)
|
|
|
|
|
META_StateAttribute(NifOsg, FFPLightStateAttribute, osg::StateAttribute::LIGHT)
|
|
|
|
|
|
|
|
|
|
void apply(osg::State& state) const override
|
|
|
|
|
{
|
|
|
|
@ -60,7 +323,7 @@ namespace SceneUtil
|
|
|
|
|
|
|
|
|
|
LightStateCache* cache = getLightStateCache(state.getContextID());
|
|
|
|
|
|
|
|
|
|
for (unsigned int i=0; i<mLights.size(); ++i)
|
|
|
|
|
for (size_t i=0; i<mLights.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
osg::Light* current = cache->lastAppliedLight[i+mIndex];
|
|
|
|
|
if (current != mLights[i].get())
|
|
|
|
@ -90,14 +353,14 @@ namespace SceneUtil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
unsigned int mIndex;
|
|
|
|
|
size_t mIndex;
|
|
|
|
|
|
|
|
|
|
std::vector<osg::ref_ptr<osg::Light> > mLights;
|
|
|
|
|
std::vector<osg::ref_ptr<osg::Light>> mLights;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LightManager* findLightManager(const osg::NodePath& path)
|
|
|
|
|
{
|
|
|
|
|
for (unsigned int i=0;i<path.size(); ++i)
|
|
|
|
|
for (size_t i=0;i<path.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
if (LightManager* lightManager = dynamic_cast<LightManager*>(path[i]))
|
|
|
|
|
return lightManager;
|
|
|
|
@ -160,33 +423,161 @@ namespace SceneUtil
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LightManager::LightManager()
|
|
|
|
|
class SunlightCallback : public osg::NodeCallback
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SunlightCallback(LightManager* lightManager) : mLightManager(lightManager) {}
|
|
|
|
|
|
|
|
|
|
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
|
|
|
|
|
{
|
|
|
|
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
|
|
|
|
|
|
|
|
|
if (mLastFrameNumber != cv->getTraversalNumber())
|
|
|
|
|
{
|
|
|
|
|
mLastFrameNumber = cv->getTraversalNumber();
|
|
|
|
|
|
|
|
|
|
auto sun = mLightManager->getSunlight();
|
|
|
|
|
|
|
|
|
|
if (!sun)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto buf = mLightManager->getSunBuffer();
|
|
|
|
|
|
|
|
|
|
buf->setValue(SunlightBuffer::Diffuse, sun->getDiffuse());
|
|
|
|
|
buf->setValue(SunlightBuffer::Ambient, sun->getAmbient());
|
|
|
|
|
buf->setValue(SunlightBuffer::Specular, sun->getSpecular());
|
|
|
|
|
buf->setValue(SunlightBuffer::Direction, sun->getPosition() * *cv->getCurrentRenderStage()->getInitialViewMatrix());
|
|
|
|
|
|
|
|
|
|
if (buf->isDirty())
|
|
|
|
|
buf->dirty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
traverse(node, nv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
LightManager* mLightManager;
|
|
|
|
|
size_t mLastFrameNumber;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LightManager::LightManager(bool ffp)
|
|
|
|
|
: mStartLight(0)
|
|
|
|
|
, mLightingMask(~0u)
|
|
|
|
|
, mSun(nullptr)
|
|
|
|
|
, mSunBuffer(nullptr)
|
|
|
|
|
, mFFP(ffp)
|
|
|
|
|
{
|
|
|
|
|
setUpdateCallback(new LightManagerUpdateCallback);
|
|
|
|
|
for (unsigned int i=0; i<8; ++i)
|
|
|
|
|
mDummies.push_back(new LightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light> >()));
|
|
|
|
|
|
|
|
|
|
if (usingFFP())
|
|
|
|
|
{
|
|
|
|
|
for (int i=0; i<getMaxLights(); ++i)
|
|
|
|
|
mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light> >()));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* stateset = getOrCreateStateSet();
|
|
|
|
|
|
|
|
|
|
mSunBuffer = new SunlightBuffer();
|
|
|
|
|
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
|
|
|
|
|
mSunBuffer->getData()->setBufferObject(ubo);
|
|
|
|
|
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(9 , mSunBuffer->getData().get(), 0, mSunBuffer->blockSize());
|
|
|
|
|
stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON);
|
|
|
|
|
|
|
|
|
|
stateset->addUniform(new osg::Uniform("PointLightCount", 0));
|
|
|
|
|
|
|
|
|
|
addCullCallback(new SunlightCallback(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LightManager::LightManager(const LightManager ©, const osg::CopyOp ©op)
|
|
|
|
|
: osg::Group(copy, copyop)
|
|
|
|
|
, mStartLight(copy.mStartLight)
|
|
|
|
|
, mLightingMask(copy.mLightingMask)
|
|
|
|
|
, mSun(copy.mSun)
|
|
|
|
|
, mSunBuffer(copy.mSunBuffer)
|
|
|
|
|
, mFFP(copy.mFFP)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LightManager::usingFFP() const
|
|
|
|
|
{
|
|
|
|
|
return mFFP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int LightManager::getMaxLights() const
|
|
|
|
|
{
|
|
|
|
|
if (usingFFP()) return LightManager::mFFPMaxLights;
|
|
|
|
|
return std::clamp(Settings::Manager::getInt("max lights", "Shaders"), 0, getMaxLightsInScene());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int LightManager::getMaxLightsInScene() const
|
|
|
|
|
{
|
|
|
|
|
static constexpr int max = 16384 / PointLightBuffer::queryBlockSize(1);
|
|
|
|
|
return max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Shader::ShaderManager::DefineMap LightManager::getLightDefines() const
|
|
|
|
|
{
|
|
|
|
|
Shader::ShaderManager::DefineMap defines;
|
|
|
|
|
|
|
|
|
|
bool ffp = usingFFP();
|
|
|
|
|
|
|
|
|
|
defines["ffpLighting"] = ffp ? "1" : "0";
|
|
|
|
|
defines["sunDirection"] = ffp ? "gl_LightSource[0].position" : "Sun.direction";
|
|
|
|
|
defines["sunAmbient"] = ffp ? "gl_LightSource[0].ambient" : "Sun.ambient";
|
|
|
|
|
defines["sunDiffuse"] = ffp ? "gl_LightSource[0].diffuse" : "Sun.diffuse";
|
|
|
|
|
defines["sunSpecular"] = ffp ? "gl_LightSource[0].specular" : "Sun.specular";
|
|
|
|
|
defines["maxLights"] = std::to_string(getMaxLights());
|
|
|
|
|
defines["maxLightsInScene"] = std::to_string(getMaxLightsInScene());
|
|
|
|
|
|
|
|
|
|
return defines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LightManager::queryNonFFPLightingSupport()
|
|
|
|
|
{
|
|
|
|
|
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
|
|
|
|
|
if (!exts || !exts->isUniformBufferObjectSupported)
|
|
|
|
|
{
|
|
|
|
|
auto ffpWarning = Misc::StringUtils::format("GL_ARB_uniform_buffer_object not supported: Falling back to FFP %zu light limit. Can not set lights to %i."
|
|
|
|
|
, LightManager::mFFPMaxLights
|
|
|
|
|
, Settings::Manager::getInt("max lights", "Shaders"));
|
|
|
|
|
Log(Debug::Warning) << ffpWarning;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LightManager::setLightingMask(unsigned int mask)
|
|
|
|
|
void LightManager::setLightingMask(size_t mask)
|
|
|
|
|
{
|
|
|
|
|
mLightingMask = mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int LightManager::getLightingMask() const
|
|
|
|
|
size_t LightManager::getLightingMask() const
|
|
|
|
|
{
|
|
|
|
|
return mLightingMask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LightManager::setStartLight(int start)
|
|
|
|
|
{
|
|
|
|
|
if (!usingFFP()) return;
|
|
|
|
|
|
|
|
|
|
mStartLight = start;
|
|
|
|
|
|
|
|
|
|
// Set default light state to zero
|
|
|
|
|
// This is necessary because shaders don't respect glDisable(GL_LIGHTX) so in addition to disabling
|
|
|
|
|
// we'll have to set a light state that has no visible effect
|
|
|
|
|
for (int i=start; i<getMaxLights(); ++i)
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<DisableLight> defaultLight (new DisableLight(i));
|
|
|
|
|
getOrCreateStateSet()->setAttributeAndModes(defaultLight, osg::StateAttribute::OFF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int LightManager::getStartLight() const
|
|
|
|
|
{
|
|
|
|
|
return mStartLight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LightManager::update()
|
|
|
|
|
{
|
|
|
|
|
mLights.clear();
|
|
|
|
@ -200,7 +591,7 @@ namespace SceneUtil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, unsigned int frameNum)
|
|
|
|
|
void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum)
|
|
|
|
|
{
|
|
|
|
|
LightSourceTransform l;
|
|
|
|
|
l.mLightSource = lightSource;
|
|
|
|
@ -211,19 +602,28 @@ namespace SceneUtil
|
|
|
|
|
mLights.push_back(l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* similar to the boost::hash_combine */
|
|
|
|
|
template <class T>
|
|
|
|
|
inline void hash_combine(std::size_t& seed, const T& v)
|
|
|
|
|
void LightManager::setSunlight(osg::ref_ptr<osg::Light> sun)
|
|
|
|
|
{
|
|
|
|
|
std::hash<T> hasher;
|
|
|
|
|
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
|
|
|
|
if (usingFFP()) return;
|
|
|
|
|
|
|
|
|
|
mSun = sun;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Light> LightManager::getSunlight()
|
|
|
|
|
{
|
|
|
|
|
return mSun;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<SunlightBuffer> LightManager::getSunBuffer()
|
|
|
|
|
{
|
|
|
|
|
return mSunBuffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::StateSet> LightManager::getLightListStateSet(const LightList &lightList, unsigned int frameNum)
|
|
|
|
|
osg::ref_ptr<osg::StateSet> LightManager::getLightListStateSet(const LightList &lightList, size_t frameNum)
|
|
|
|
|
{
|
|
|
|
|
// possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists)
|
|
|
|
|
size_t hash = 0;
|
|
|
|
|
for (unsigned int i=0; i<lightList.size();++i)
|
|
|
|
|
for (size_t i=0; i<lightList.size();++i)
|
|
|
|
|
hash_combine(hash, lightList[i]->mLightSource->getId());
|
|
|
|
|
|
|
|
|
|
LightStateSetMap& stateSetCache = mStateSetCache[frameNum%2];
|
|
|
|
@ -236,21 +636,35 @@ namespace SceneUtil
|
|
|
|
|
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
|
|
|
|
std::vector<osg::ref_ptr<osg::Light> > lights;
|
|
|
|
|
lights.reserve(lightList.size());
|
|
|
|
|
for (unsigned int i=0; i<lightList.size();++i)
|
|
|
|
|
for (size_t i=0; i<lightList.size();++i)
|
|
|
|
|
lights.emplace_back(lightList[i]->mLightSource->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!
|
|
|
|
|
stateset->setAttribute(new LightStateAttribute(mStartLight, std::move(lights)), osg::StateAttribute::ON);
|
|
|
|
|
|
|
|
|
|
for (unsigned int i=0; i<lightList.size(); ++i)
|
|
|
|
|
stateset->setMode(GL_LIGHT0 + mStartLight + i, osg::StateAttribute::ON);
|
|
|
|
|
|
|
|
|
|
// need to push some dummy attributes to ensure proper state tracking
|
|
|
|
|
// lights need to reset to their default when the StateSet is popped
|
|
|
|
|
for (unsigned int i=1; i<lightList.size(); ++i)
|
|
|
|
|
stateset->setAttribute(mDummies[i+mStartLight].get(), osg::StateAttribute::ON);
|
|
|
|
|
if (usingFFP())
|
|
|
|
|
{
|
|
|
|
|
// 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!
|
|
|
|
|
stateset->setAttribute(new FFPLightStateAttribute(mStartLight, std::move(lights)), osg::StateAttribute::ON);
|
|
|
|
|
|
|
|
|
|
for (size_t i=0; i<lightList.size(); ++i)
|
|
|
|
|
stateset->setMode(GL_LIGHT0 + mStartLight + i, osg::StateAttribute::ON);
|
|
|
|
|
|
|
|
|
|
// need to push some dummy attributes to ensure proper state tracking
|
|
|
|
|
// lights need to reset to their default when the StateSet is popped
|
|
|
|
|
for (size_t i=1; i<lightList.size(); ++i)
|
|
|
|
|
stateset->setAttribute(mDummies[i+mStartLight].get(), osg::StateAttribute::ON);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<PointLightBuffer> buffer = new PointLightBuffer(lights.size());
|
|
|
|
|
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
|
|
|
|
|
buffer->getData()->setBufferObject(ubo);
|
|
|
|
|
osg::ref_ptr<osg::UniformBufferBinding> 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stateSetCache.emplace(hash, stateset);
|
|
|
|
|
return stateset;
|
|
|
|
@ -262,7 +676,7 @@ namespace SceneUtil
|
|
|
|
|
return mLights;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix)
|
|
|
|
|
const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum)
|
|
|
|
|
{
|
|
|
|
|
osg::observer_ptr<osg::Camera> camPtr (camera);
|
|
|
|
|
std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection>::iterator it = mLightsInViewSpace.find(camPtr);
|
|
|
|
@ -286,75 +700,6 @@ namespace SceneUtil
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class DisableLight : public osg::StateAttribute
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DisableLight() : mIndex(0) {}
|
|
|
|
|
DisableLight(int index) : mIndex(index) {}
|
|
|
|
|
|
|
|
|
|
DisableLight(const DisableLight& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
|
|
|
|
: osg::StateAttribute(copy,copyop), mIndex(copy.mIndex) {}
|
|
|
|
|
|
|
|
|
|
osg::Object* cloneType() const override { return new DisableLight(mIndex); }
|
|
|
|
|
osg::Object* clone(const osg::CopyOp& copyop) const override { return new DisableLight(*this,copyop); }
|
|
|
|
|
bool isSameKindAs(const osg::Object* obj) const override { return dynamic_cast<const DisableLight *>(obj)!=nullptr; }
|
|
|
|
|
const char* libraryName() const override { return "SceneUtil"; }
|
|
|
|
|
const char* className() const override { return "DisableLight"; }
|
|
|
|
|
Type getType() const override { return LIGHT; }
|
|
|
|
|
|
|
|
|
|
unsigned int getMember() const override
|
|
|
|
|
{
|
|
|
|
|
return mIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool getModeUsage(ModeUsage & usage) const override
|
|
|
|
|
{
|
|
|
|
|
usage.usesMode(GL_LIGHT0 + mIndex);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int compare(const StateAttribute &sa) const override
|
|
|
|
|
{
|
|
|
|
|
throw std::runtime_error("DisableLight::compare: unimplemented");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void apply(osg::State& state) const override
|
|
|
|
|
{
|
|
|
|
|
int lightNum = GL_LIGHT0 + mIndex;
|
|
|
|
|
glLightfv( lightNum, GL_AMBIENT, mnullptr.ptr() );
|
|
|
|
|
glLightfv( lightNum, GL_DIFFUSE, mnullptr.ptr() );
|
|
|
|
|
glLightfv( lightNum, GL_SPECULAR, mnullptr.ptr() );
|
|
|
|
|
|
|
|
|
|
LightStateCache* cache = getLightStateCache(state.getContextID());
|
|
|
|
|
cache->lastAppliedLight[mIndex] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
unsigned int mIndex;
|
|
|
|
|
osg::Vec4f mnullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void LightManager::setStartLight(int start)
|
|
|
|
|
{
|
|
|
|
|
mStartLight = start;
|
|
|
|
|
|
|
|
|
|
// Set default light state to zero
|
|
|
|
|
// This is necessary because shaders don't respect glDisable(GL_LIGHTX) so in addition to disabling
|
|
|
|
|
// we'll have to set a light state that has no visible effect
|
|
|
|
|
for (int i=start; i<8; ++i)
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<DisableLight> defaultLight (new DisableLight(i));
|
|
|
|
|
getOrCreateStateSet()->setAttributeAndModes(defaultLight, osg::StateAttribute::OFF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int LightManager::getStartLight() const
|
|
|
|
|
{
|
|
|
|
|
return mStartLight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sLightId = 0;
|
|
|
|
|
|
|
|
|
|
LightSource::LightSource()
|
|
|
|
|
: mRadius(0.f)
|
|
|
|
|
{
|
|
|
|
@ -372,12 +717,6 @@ namespace SceneUtil
|
|
|
|
|
mLight[i] = new osg::Light(*copy.mLight[i].get(), copyop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool sortLights (const LightManager::LightSourceViewBound* left, const LightManager::LightSourceViewBound* right)
|
|
|
|
|
{
|
|
|
|
|
return left->mViewBound.center().length2() - left->mViewBound.radius2()*81 < right->mViewBound.center().length2() - right->mViewBound.radius2()*81;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv)
|
|
|
|
|
{
|
|
|
|
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
|
|
|
@ -413,7 +752,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<LightManager::LightSourceViewBound>& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix);
|
|
|
|
|
const std::vector<LightManager::LightSourceViewBound>& 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
|
|
|
|
@ -421,7 +760,7 @@ namespace SceneUtil
|
|
|
|
|
osg::Transform* transform = node->asTransform();
|
|
|
|
|
if (transform)
|
|
|
|
|
{
|
|
|
|
|
for (unsigned int i=0; i<transform->getNumChildren(); ++i)
|
|
|
|
|
for (size_t i=0; i<transform->getNumChildren(); ++i)
|
|
|
|
|
nodeBound.expandBy(transform->getChild(i)->getBound());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@ -430,7 +769,7 @@ namespace SceneUtil
|
|
|
|
|
transformBoundingSphere(mat, nodeBound);
|
|
|
|
|
|
|
|
|
|
mLightList.clear();
|
|
|
|
|
for (unsigned int i=0; i<lights.size(); ++i)
|
|
|
|
|
for (size_t i=0; i<lights.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
const LightManager::LightSourceViewBound& l = lights[i];
|
|
|
|
|
|
|
|
|
@ -443,7 +782,7 @@ namespace SceneUtil
|
|
|
|
|
}
|
|
|
|
|
if (!mLightList.empty())
|
|
|
|
|
{
|
|
|
|
|
unsigned int maxLights = static_cast<unsigned int> (8 - mLightManager->getStartLight());
|
|
|
|
|
size_t maxLights = mLightManager->getMaxLights() - mLightManager->getStartLight();
|
|
|
|
|
|
|
|
|
|
osg::StateSet* stateset = nullptr;
|
|
|
|
|
|
|
|
|
|