mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 22:15:32 +00:00
*very* early version of the LightManager
This commit is contained in:
parent
642c1d2d36
commit
591a35b8d7
8 changed files with 542 additions and 66 deletions
|
@ -39,7 +39,7 @@ add_component_dir (resource
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (sceneutil
|
add_component_dir (sceneutil
|
||||||
clone attach
|
clone attach lightmanager visitor util
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (nif
|
add_component_dir (nif
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <osg/TexEnvCombine>
|
#include <osg/TexEnvCombine>
|
||||||
|
|
||||||
#include <components/nif/node.hpp>
|
#include <components/nif/node.hpp>
|
||||||
|
#include <components/sceneutil/util.hpp>
|
||||||
|
|
||||||
#include "particle.hpp"
|
#include "particle.hpp"
|
||||||
#include "userdata.hpp"
|
#include "userdata.hpp"
|
||||||
|
@ -467,7 +468,9 @@ namespace
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual osg::BoundingBox computeBound(const osg::Drawable& drawable) const
|
META_Object(NifOsg, StaticBoundingBoxCallback)
|
||||||
|
|
||||||
|
virtual osg::BoundingBox computeBound(const osg::Drawable&) const
|
||||||
{
|
{
|
||||||
return mBoundingBox;
|
return mBoundingBox;
|
||||||
}
|
}
|
||||||
|
@ -497,37 +500,6 @@ namespace
|
||||||
mBoundSpheres[bonename] = sphere;
|
mBoundSpheres[bonename] = sphere;
|
||||||
}
|
}
|
||||||
|
|
||||||
// based off code in osg::Transform
|
|
||||||
void transformBoundingSphere (const osg::Matrix& matrix, osg::BoundingSphere& bsphere) const
|
|
||||||
{
|
|
||||||
osg::BoundingSphere::vec_type xdash = bsphere._center;
|
|
||||||
xdash.x() += bsphere._radius;
|
|
||||||
xdash = xdash*matrix;
|
|
||||||
|
|
||||||
osg::BoundingSphere::vec_type ydash = bsphere._center;
|
|
||||||
ydash.y() += bsphere._radius;
|
|
||||||
ydash = ydash*matrix;
|
|
||||||
|
|
||||||
osg::BoundingSphere::vec_type zdash = bsphere._center;
|
|
||||||
zdash.z() += bsphere._radius;
|
|
||||||
zdash = zdash*matrix;
|
|
||||||
|
|
||||||
bsphere._center = bsphere._center*matrix;
|
|
||||||
|
|
||||||
xdash -= bsphere._center;
|
|
||||||
osg::BoundingSphere::value_type len_xdash = xdash.length();
|
|
||||||
|
|
||||||
ydash -= bsphere._center;
|
|
||||||
osg::BoundingSphere::value_type len_ydash = ydash.length();
|
|
||||||
|
|
||||||
zdash -= bsphere._center;
|
|
||||||
osg::BoundingSphere::value_type len_zdash = zdash.length();
|
|
||||||
|
|
||||||
bsphere._radius = len_xdash;
|
|
||||||
if (bsphere._radius<len_ydash) bsphere._radius = len_ydash;
|
|
||||||
if (bsphere._radius<len_zdash) bsphere._radius = len_zdash;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual osg::BoundingBox computeBound(const osg::Drawable& drawable) const
|
virtual osg::BoundingBox computeBound(const osg::Drawable& drawable) const
|
||||||
{
|
{
|
||||||
osg::BoundingBox box;
|
osg::BoundingBox box;
|
||||||
|
@ -549,7 +521,7 @@ namespace
|
||||||
{
|
{
|
||||||
osgAnimation::Bone* bone = it->first;
|
osgAnimation::Bone* bone = it->first;
|
||||||
osg::BoundingSphere bs = it->second;
|
osg::BoundingSphere bs = it->second;
|
||||||
transformBoundingSphere(bone->getMatrixInSkeletonSpace(), bs);
|
SceneUtil::transformBoundingSphere(bone->getMatrixInSkeletonSpace(), bs);
|
||||||
box.expandBy(bs);
|
box.expandBy(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,7 +748,6 @@ namespace NifOsg
|
||||||
osgAnimation::Bone* bone = new osgAnimation::Bone;
|
osgAnimation::Bone* bone = new osgAnimation::Bone;
|
||||||
transformNode = bone;
|
transformNode = bone;
|
||||||
bone->setMatrix(toMatrix(nifNode->trafo));
|
bone->setMatrix(toMatrix(nifNode->trafo));
|
||||||
bone->setName(nifNode->name);
|
|
||||||
bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode)));
|
bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -788,14 +759,14 @@ namespace NifOsg
|
||||||
transformNode->addCullCallback(new BillboardCallback);
|
transformNode->addCullCallback(new BillboardCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformNode->setName(nifNode->name);
|
||||||
|
|
||||||
if (parentNode)
|
if (parentNode)
|
||||||
parentNode->addChild(transformNode);
|
parentNode->addChild(transformNode);
|
||||||
|
|
||||||
if (!rootNode)
|
if (!rootNode)
|
||||||
rootNode = transformNode;
|
rootNode = transformNode;
|
||||||
|
|
||||||
// Ignoring name for non-bone nodes for now. We might need it later in isolated cases, e.g. AttachLight.
|
|
||||||
|
|
||||||
// UserData used for a variety of features:
|
// UserData used for a variety of features:
|
||||||
// - finding the correct emitter node for a particle system
|
// - finding the correct emitter node for a particle system
|
||||||
// - establishing connections to the animated collision shapes, which are handled in a separate loader
|
// - establishing connections to the animated collision shapes, which are handled in a separate loader
|
||||||
|
|
|
@ -12,40 +12,39 @@
|
||||||
#include <osgAnimation/BoneMapVisitor>
|
#include <osgAnimation/BoneMapVisitor>
|
||||||
#include <osgAnimation/Skeleton>
|
#include <osgAnimation/Skeleton>
|
||||||
|
|
||||||
|
#include "visitor.hpp"
|
||||||
|
|
||||||
namespace SceneUtil
|
namespace SceneUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
class FindByNameVisitor : public osg::NodeVisitor
|
class NodeMapVisitor : public osg::NodeVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FindByNameVisitor(const std::string& nameToFind)
|
NodeMapVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
|
||||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
|
||||||
, mNameToFind(nameToFind)
|
void apply(osg::MatrixTransform& trans)
|
||||||
, mFoundNode(NULL)
|
|
||||||
{
|
{
|
||||||
|
mMap[trans.getName()] = &trans;
|
||||||
|
traverse(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void apply(osg::Node &node)
|
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
|
||||||
|
|
||||||
|
const NodeMap& getNodeMap() const
|
||||||
{
|
{
|
||||||
osg::Group* group = node.asGroup();
|
return mMap;
|
||||||
if (group && node.getName() == mNameToFind)
|
|
||||||
{
|
|
||||||
mFoundNode = group;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
traverse(node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& mNameToFind;
|
private:
|
||||||
osg::Group* mFoundNode;
|
NodeMap mMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Copy the skeleton-space matrix of a "source" bone to a "dest" bone (the bone that the callback is attached to).
|
/// Copy the matrix of a "source" node to a "dest" node (the node that the callback is attached to).
|
||||||
/// Must be set on a Bone.
|
/// Must be set on a MatrixTransform.
|
||||||
class CopyController : public osg::NodeCallback
|
class CopyController : public osg::NodeCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CopyController(osgAnimation::Bone* copyFrom)
|
CopyController(osg::MatrixTransform* copyFrom)
|
||||||
: mCopyFrom(copyFrom)
|
: mCopyFrom(copyFrom)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -66,22 +65,21 @@ namespace SceneUtil
|
||||||
if (mCopyFrom)
|
if (mCopyFrom)
|
||||||
{
|
{
|
||||||
bone->setMatrix(mCopyFrom->getMatrix());
|
bone->setMatrix(mCopyFrom->getMatrix());
|
||||||
bone->setMatrixInSkeletonSpace(mCopyFrom->getMatrixInSkeletonSpace());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse(node, nv);
|
traverse(node, nv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const osgAnimation::Bone* mCopyFrom;
|
const osg::MatrixTransform* mCopyFrom;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddCopyControllerVisitor : public osg::NodeVisitor
|
class AddCopyControllerVisitor : public osg::NodeVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AddCopyControllerVisitor(const osgAnimation::BoneMap& boneMap)
|
AddCopyControllerVisitor(const NodeMapVisitor::NodeMap& boneMap)
|
||||||
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
, mBoneMap(boneMap)
|
, mNodeMap(boneMap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,19 +87,23 @@ namespace SceneUtil
|
||||||
{
|
{
|
||||||
if (osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&node))
|
if (osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&node))
|
||||||
{
|
{
|
||||||
osgAnimation::BoneMap::const_iterator found = mBoneMap.find(bone->getName());
|
NodeMapVisitor::NodeMap::const_iterator found = mNodeMap.find(bone->getName());
|
||||||
if (found != mBoneMap.end())
|
if (found != mNodeMap.end())
|
||||||
{
|
{
|
||||||
|
// add the CopyController at position 0 so it's executed before UpdateBone
|
||||||
|
osg::ref_ptr<osg::NodeCallback> old = bone->getUpdateCallback();
|
||||||
bone->setUpdateCallback(new CopyController(found->second.get()));
|
bone->setUpdateCallback(new CopyController(found->second.get()));
|
||||||
|
bone->addUpdateCallback(old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const osgAnimation::BoneMap& mBoneMap;
|
const NodeMapVisitor::NodeMap& mNodeMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: would be more efficient to copy only the wanted nodes instead of deleting unwanted ones later
|
// FIXME: would be more efficient to copy only the wanted nodes instead of deleting unwanted ones later
|
||||||
|
// copying is kinda cheap though, so don't bother for now
|
||||||
class FilterVisitor : public osg::NodeVisitor
|
class FilterVisitor : public osg::NodeVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -137,13 +139,12 @@ namespace SceneUtil
|
||||||
{
|
{
|
||||||
if (osgAnimation::Skeleton* skel = dynamic_cast<osgAnimation::Skeleton*>(toAttach.get()))
|
if (osgAnimation::Skeleton* skel = dynamic_cast<osgAnimation::Skeleton*>(toAttach.get()))
|
||||||
{
|
{
|
||||||
osgAnimation::Skeleton* masterSkel = dynamic_cast<osgAnimation::Skeleton*>(master);
|
NodeMapVisitor nodeMapVisitor;
|
||||||
osgAnimation::BoneMapVisitor boneMapVisitor;
|
master->accept(nodeMapVisitor);
|
||||||
masterSkel->accept(boneMapVisitor);
|
|
||||||
|
|
||||||
// would be more efficient if we could attach the RigGeometry directly to the master skeleton, but currently not possible
|
// would be more efficient if we could attach the RigGeometry directly to the master skeleton, but currently not possible
|
||||||
// due to a difference in binding pose of the two skeletons
|
// due to a difference in binding pose of the two skeletons
|
||||||
AddCopyControllerVisitor visitor(boneMapVisitor.getBoneMap());
|
AddCopyControllerVisitor visitor(nodeMapVisitor.getNodeMap());
|
||||||
toAttach->accept(visitor);
|
toAttach->accept(visitor);
|
||||||
|
|
||||||
FilterVisitor filterVisitor(filter);
|
FilterVisitor filterVisitor(filter);
|
||||||
|
|
309
components/sceneutil/lightmanager.cpp
Normal file
309
components/sceneutil/lightmanager.cpp
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
#include "lightmanager.hpp"
|
||||||
|
|
||||||
|
#include <osg/NodeVisitor>
|
||||||
|
#include <osg/Geode>
|
||||||
|
|
||||||
|
#include <osgUtil/CullVisitor>
|
||||||
|
|
||||||
|
#include <components/sceneutil/util.hpp>
|
||||||
|
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
class LightStateAttribute : public osg::Light
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LightStateAttribute() {}
|
||||||
|
|
||||||
|
LightStateAttribute(const LightStateAttribute& light,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
||||||
|
: osg::Light(light,copyop) {}
|
||||||
|
|
||||||
|
LightStateAttribute(const osg::Light& light,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
|
||||||
|
: osg::Light(light,copyop) {}
|
||||||
|
|
||||||
|
|
||||||
|
META_StateAttribute(NifOsg, LightStateAttribute, osg::StateAttribute::LIGHT)
|
||||||
|
|
||||||
|
virtual void apply(osg::State& state) const
|
||||||
|
{
|
||||||
|
osg::Matrix modelViewMatrix = state.getModelViewMatrix();
|
||||||
|
|
||||||
|
// FIXME: we shouldn't have to apply the modelview matrix after every light
|
||||||
|
// this could be solvable by having the LightStateAttribute contain all lights instead of just one.
|
||||||
|
state.applyModelViewMatrix(state.getInitialViewMatrix());
|
||||||
|
|
||||||
|
osg::Light::apply(state);
|
||||||
|
|
||||||
|
state.setGlobalDefaultAttribute(this);
|
||||||
|
|
||||||
|
state.applyModelViewMatrix(modelViewMatrix);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CullCallback : public osg::NodeCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CullCallback()
|
||||||
|
: mLightManager(NULL)
|
||||||
|
{}
|
||||||
|
CullCallback(const CullCallback& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY)
|
||||||
|
: osg::Object(copy, copyop), osg::NodeCallback(copy, copyop), mLightManager(copy.mLightManager)
|
||||||
|
{}
|
||||||
|
CullCallback(LightManager* lightManager)
|
||||||
|
: mLightManager(lightManager)
|
||||||
|
{}
|
||||||
|
|
||||||
|
META_Object(NifOsg, CullCallback)
|
||||||
|
|
||||||
|
void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
||||||
|
|
||||||
|
mLightManager->prepareForCamera(cv->getCurrentCamera());
|
||||||
|
|
||||||
|
// Possible optimizations:
|
||||||
|
// - cull list of lights by the camera frustum
|
||||||
|
// - organize lights in a quad tree
|
||||||
|
|
||||||
|
const std::vector<LightManager::LightSourceTransform>& lights = mLightManager->getLights();
|
||||||
|
|
||||||
|
if (lights.size())
|
||||||
|
{
|
||||||
|
// we do the intersections in view space
|
||||||
|
osg::BoundingSphere nodeBound = node->getBound();
|
||||||
|
osg::Matrixf mat = *cv->getModelViewMatrix();
|
||||||
|
transformBoundingSphere(mat, nodeBound);
|
||||||
|
|
||||||
|
std::vector<int> lightList;
|
||||||
|
for (unsigned int i=0; i<lights.size(); ++i)
|
||||||
|
{
|
||||||
|
const LightManager::LightSourceTransform& l = lights[i];
|
||||||
|
if (l.mViewBound.intersects(nodeBound))
|
||||||
|
lightList.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lightList.empty())
|
||||||
|
{
|
||||||
|
traverse(node, nv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lightList.size() > 8)
|
||||||
|
{
|
||||||
|
//std::cerr << "More than 8 lights!" << std::endl;
|
||||||
|
|
||||||
|
// TODO: sort lights by certain criteria
|
||||||
|
|
||||||
|
while (lightList.size() > 8)
|
||||||
|
lightList.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> stateset = mLightManager->getLightListStateSet(lightList);
|
||||||
|
|
||||||
|
cv->pushStateSet(stateset);
|
||||||
|
|
||||||
|
traverse(node, nv);
|
||||||
|
|
||||||
|
cv->popStateSet();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LightManager* mLightManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AttachCullCallbackVisitor : public osg::NodeVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AttachCullCallbackVisitor(LightManager* lightManager)
|
||||||
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
|
, mLightManager(lightManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void apply(osg::Geode &geode)
|
||||||
|
{
|
||||||
|
if (!geode.getNumParents())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Not working on a Geode. Drawables are not normal children of the Geode, the traverse() call
|
||||||
|
// does not traverse the drawables, so the push/pop in the CullCallback has no effect
|
||||||
|
// Should be no longer an issue with osg trunk
|
||||||
|
osg::Node* parent = geode.getParent(0);
|
||||||
|
parent->addCullCallback(new CullCallback(mLightManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LightManager* mLightManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CollectLightCallback()
|
||||||
|
: mLightManager(0) { }
|
||||||
|
|
||||||
|
CollectLightCallback(const CollectLightCallback& copy, const osg::CopyOp& copyop)
|
||||||
|
: osg::NodeCallback(copy, copyop)
|
||||||
|
, mLightManager(0) { }
|
||||||
|
|
||||||
|
META_Object(SceneUtil, SceneUtil::CollectLightCallback)
|
||||||
|
|
||||||
|
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
if (!mLightManager)
|
||||||
|
{
|
||||||
|
for (unsigned int i=0;i<nv->getNodePath().size(); ++i)
|
||||||
|
{
|
||||||
|
if (LightManager* lightManager = dynamic_cast<LightManager*>(nv->getNodePath()[i]))
|
||||||
|
{
|
||||||
|
mLightManager = lightManager;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!mLightManager)
|
||||||
|
throw std::runtime_error("can't find parent LightManager");
|
||||||
|
}
|
||||||
|
|
||||||
|
mLightManager->addLight(static_cast<LightSource*>(node), osg::computeLocalToWorld(nv->getNodePath()));
|
||||||
|
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LightManager* mLightManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set on a LightManager. Clears the data from the previous frame.
|
||||||
|
class LightManagerUpdateCallback : public osg::NodeCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LightManagerUpdateCallback()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
LightManagerUpdateCallback(const LightManagerUpdateCallback& copy, const osg::CopyOp& copyop)
|
||||||
|
: osg::NodeCallback(copy, copyop)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
META_Object(SceneUtil, SceneUtil::LightManagerUpdateCallback)
|
||||||
|
|
||||||
|
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
|
{
|
||||||
|
LightManager* lightManager = static_cast<LightManager*>(node);
|
||||||
|
lightManager->update();
|
||||||
|
|
||||||
|
traverse(node, nv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
LightManager::LightManager()
|
||||||
|
: mLightsInViewSpace(false)
|
||||||
|
, mDecorated(false)
|
||||||
|
{
|
||||||
|
setUpdateCallback(new LightManagerUpdateCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
LightManager::LightManager(const LightManager ©, const osg::CopyOp ©op)
|
||||||
|
: osg::Group(copy, copyop)
|
||||||
|
, mLightsInViewSpace(false)
|
||||||
|
, mDecorated(copy.mDecorated)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightManager::decorateGeodes()
|
||||||
|
{
|
||||||
|
AttachCullCallbackVisitor visitor(this);
|
||||||
|
accept(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightManager::update()
|
||||||
|
{
|
||||||
|
mLightsInViewSpace = false;
|
||||||
|
mLights.clear();
|
||||||
|
mStateSetCache.clear();
|
||||||
|
|
||||||
|
if (!mDecorated)
|
||||||
|
{
|
||||||
|
decorateGeodes();
|
||||||
|
mDecorated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightManager::addLight(LightSource* lightSource, osg::Matrix worldMat)
|
||||||
|
{
|
||||||
|
LightSourceTransform l;
|
||||||
|
l.mLightSource = lightSource;
|
||||||
|
l.mWorldMatrix = worldMat;
|
||||||
|
mLights.push_back(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightManager::prepareForCamera(osg::Camera *cam)
|
||||||
|
{
|
||||||
|
// later on we need to store this per camera
|
||||||
|
if (!mLightsInViewSpace)
|
||||||
|
{
|
||||||
|
for (std::vector<LightSourceTransform>::iterator it = mLights.begin(); it != mLights.end(); ++it)
|
||||||
|
{
|
||||||
|
LightSourceTransform& l = *it;
|
||||||
|
osg::Matrix worldViewMat = l.mWorldMatrix * cam->getViewMatrix();
|
||||||
|
l.mViewBound = osg::BoundingSphere(osg::Vec3f(0,0,0), l.mLightSource->getRadius());
|
||||||
|
transformBoundingSphere(worldViewMat, l.mViewBound);
|
||||||
|
}
|
||||||
|
mLightsInViewSpace = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> LightManager::getLightListStateSet(const LightList &lightList)
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
boost::hash_combine(hash, lightList[i]);
|
||||||
|
|
||||||
|
LightStateSetMap::iterator found = mStateSetCache.find(hash);
|
||||||
|
if (found != mStateSetCache.end())
|
||||||
|
return found->second;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
|
||||||
|
for (unsigned int i=0; i<lightList.size();++i)
|
||||||
|
{
|
||||||
|
int lightIndex = lightList[i];
|
||||||
|
osg::Light* light = mLights[lightIndex].mLightSource->getLight();
|
||||||
|
|
||||||
|
osg::ref_ptr<LightStateAttribute> clonedLight = new LightStateAttribute(*light, osg::CopyOp::DEEP_COPY_ALL);
|
||||||
|
clonedLight->setPosition(mLights[lightIndex].mWorldMatrix.preMult(light->getPosition()));
|
||||||
|
|
||||||
|
clonedLight->setLightNum(i);
|
||||||
|
|
||||||
|
// don't use setAttributeAndModes, that does not support light indices!
|
||||||
|
stateset->setAttribute(clonedLight, osg::StateAttribute::ON);
|
||||||
|
stateset->setAssociatedModes(clonedLight, osg::StateAttribute::ON);
|
||||||
|
}
|
||||||
|
mStateSetCache.insert(std::make_pair(hash, stateset));
|
||||||
|
return stateset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<LightManager::LightSourceTransform>& LightManager::getLights() const
|
||||||
|
{
|
||||||
|
return mLights;
|
||||||
|
}
|
||||||
|
|
||||||
|
LightSource::LightSource()
|
||||||
|
: mRadius(0.f)
|
||||||
|
{
|
||||||
|
setUpdateCallback(new CollectLightCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
105
components/sceneutil/lightmanager.hpp
Normal file
105
components/sceneutil/lightmanager.hpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H
|
||||||
|
#define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H
|
||||||
|
|
||||||
|
#include <osg/Light>
|
||||||
|
|
||||||
|
#include <osg/Group>
|
||||||
|
#include <osg/NodeVisitor>
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
/// LightSource managed by a LightManager.
|
||||||
|
class LightSource : public osg::Node
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Light> mLight;
|
||||||
|
|
||||||
|
// The activation radius
|
||||||
|
float mRadius;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
META_Node(SceneUtil, SceneUtil::LightSource)
|
||||||
|
|
||||||
|
LightSource();
|
||||||
|
|
||||||
|
LightSource(const LightSource& copy, const osg::CopyOp& copyop)
|
||||||
|
: osg::Node(copy, copyop)
|
||||||
|
, mLight(copy.mLight)
|
||||||
|
, mRadius(copy.mRadius)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float getRadius() const
|
||||||
|
{
|
||||||
|
return mRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRadius(float radius)
|
||||||
|
{
|
||||||
|
mRadius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Light* getLight()
|
||||||
|
{
|
||||||
|
return mLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLight(osg::Light* light)
|
||||||
|
{
|
||||||
|
mLight = light;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// All light sources must be a child of the LightManager node. The LightManager can be anywhere in the scene graph,
|
||||||
|
/// but would be typically somewhere near the top.
|
||||||
|
class LightManager : public osg::Group
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
META_Node(SceneUtil, SceneUtil::LightManager)
|
||||||
|
|
||||||
|
LightManager();
|
||||||
|
|
||||||
|
LightManager(const LightManager& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
|
// Called automatically by the UpdateCallback
|
||||||
|
void update();
|
||||||
|
|
||||||
|
// Called automatically by the LightSource's UpdateCallback
|
||||||
|
void addLight(LightSource* lightSource, osg::Matrix worldMat);
|
||||||
|
|
||||||
|
void prepareForCamera(osg::Camera* cam);
|
||||||
|
|
||||||
|
void decorateGeodes();
|
||||||
|
|
||||||
|
struct LightSourceTransform
|
||||||
|
{
|
||||||
|
LightSource* mLightSource;
|
||||||
|
osg::Matrix mWorldMatrix;
|
||||||
|
osg::BoundingSphere mViewBound;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<LightSourceTransform>& getLights() const;
|
||||||
|
|
||||||
|
// Stores indices into the mLights vector
|
||||||
|
typedef std::vector<int> LightList;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> getLightListStateSet(const LightList& lightList);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Lights collected from the scene graph. Only valid during the cull traversal.
|
||||||
|
std::vector<LightSourceTransform> mLights;
|
||||||
|
|
||||||
|
bool mLightsInViewSpace;
|
||||||
|
|
||||||
|
// < Light list hash , StateSet >
|
||||||
|
typedef std::map<size_t, osg::ref_ptr<osg::StateSet> > LightStateSetMap;
|
||||||
|
LightStateSetMap mStateSetCache;
|
||||||
|
|
||||||
|
bool mDecorated;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
36
components/sceneutil/util.cpp
Normal file
36
components/sceneutil/util.cpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#include "util.hpp"
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
void transformBoundingSphere (const osg::Matrix& matrix, osg::BoundingSphere& bsphere)
|
||||||
|
{
|
||||||
|
osg::BoundingSphere::vec_type xdash = bsphere._center;
|
||||||
|
xdash.x() += bsphere._radius;
|
||||||
|
xdash = xdash*matrix;
|
||||||
|
|
||||||
|
osg::BoundingSphere::vec_type ydash = bsphere._center;
|
||||||
|
ydash.y() += bsphere._radius;
|
||||||
|
ydash = ydash*matrix;
|
||||||
|
|
||||||
|
osg::BoundingSphere::vec_type zdash = bsphere._center;
|
||||||
|
zdash.z() += bsphere._radius;
|
||||||
|
zdash = zdash*matrix;
|
||||||
|
|
||||||
|
bsphere._center = bsphere._center*matrix;
|
||||||
|
|
||||||
|
xdash -= bsphere._center;
|
||||||
|
osg::BoundingSphere::value_type len_xdash = xdash.length();
|
||||||
|
|
||||||
|
ydash -= bsphere._center;
|
||||||
|
osg::BoundingSphere::value_type len_ydash = ydash.length();
|
||||||
|
|
||||||
|
zdash -= bsphere._center;
|
||||||
|
osg::BoundingSphere::value_type len_zdash = zdash.length();
|
||||||
|
|
||||||
|
bsphere._radius = len_xdash;
|
||||||
|
if (bsphere._radius<len_ydash) bsphere._radius = len_ydash;
|
||||||
|
if (bsphere._radius<len_zdash) bsphere._radius = len_zdash;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
components/sceneutil/util.hpp
Normal file
17
components/sceneutil/util.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_SCENEUTIL_UTIL_H
|
||||||
|
#define OPENMW_COMPONENTS_SCENEUTIL_UTIL_H
|
||||||
|
|
||||||
|
#include <osg/Matrix>
|
||||||
|
#include <osg/BoundingSphere>
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
// Transform a bounding sphere by a matrix
|
||||||
|
// based off private code in osg::Transform
|
||||||
|
// TODO: patch osg to make public
|
||||||
|
void transformBoundingSphere (const osg::Matrix& matrix, osg::BoundingSphere& bsphere);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
37
components/sceneutil/visitor.hpp
Normal file
37
components/sceneutil/visitor.hpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H
|
||||||
|
#define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H
|
||||||
|
|
||||||
|
#include <osg/NodeVisitor>
|
||||||
|
|
||||||
|
// Commonly used scene graph visitors
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
class FindByNameVisitor : public osg::NodeVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FindByNameVisitor(const std::string& nameToFind)
|
||||||
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
|
, mNameToFind(nameToFind)
|
||||||
|
, mFoundNode(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void apply(osg::Node &node)
|
||||||
|
{
|
||||||
|
osg::Group* group = node.asGroup();
|
||||||
|
if (group && node.getName() == mNameToFind)
|
||||||
|
{
|
||||||
|
mFoundNode = group;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
traverse(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& mNameToFind;
|
||||||
|
osg::Group* mFoundNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue