*very* early version of the LightManager

pull/638/head
scrawl 10 years ago
parent 642c1d2d36
commit 591a35b8d7

@ -39,7 +39,7 @@ add_component_dir (resource
)
add_component_dir (sceneutil
clone attach
clone attach lightmanager visitor util
)
add_component_dir (nif

@ -41,6 +41,7 @@
#include <osg/TexEnvCombine>
#include <components/nif/node.hpp>
#include <components/sceneutil/util.hpp>
#include "particle.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;
}
@ -497,37 +500,6 @@ namespace
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
{
osg::BoundingBox box;
@ -549,7 +521,7 @@ namespace
{
osgAnimation::Bone* bone = it->first;
osg::BoundingSphere bs = it->second;
transformBoundingSphere(bone->getMatrixInSkeletonSpace(), bs);
SceneUtil::transformBoundingSphere(bone->getMatrixInSkeletonSpace(), bs);
box.expandBy(bs);
}
@ -776,7 +748,6 @@ namespace NifOsg
osgAnimation::Bone* bone = new osgAnimation::Bone;
transformNode = bone;
bone->setMatrix(toMatrix(nifNode->trafo));
bone->setName(nifNode->name);
bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode)));
}
else
@ -788,14 +759,14 @@ namespace NifOsg
transformNode->addCullCallback(new BillboardCallback);
}
transformNode->setName(nifNode->name);
if (parentNode)
parentNode->addChild(transformNode);
if (!rootNode)
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:
// - finding the correct emitter node for a particle system
// - establishing connections to the animated collision shapes, which are handled in a separate loader

@ -12,40 +12,39 @@
#include <osgAnimation/BoneMapVisitor>
#include <osgAnimation/Skeleton>
#include "visitor.hpp"
namespace SceneUtil
{
class FindByNameVisitor : public osg::NodeVisitor
class NodeMapVisitor : public osg::NodeVisitor
{
public:
FindByNameVisitor(const std::string& nameToFind)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mNameToFind(nameToFind)
, mFoundNode(NULL)
NodeMapVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
void apply(osg::MatrixTransform& trans)
{
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();
if (group && node.getName() == mNameToFind)
{
mFoundNode = group;
return;
}
traverse(node);
return mMap;
}
const std::string& mNameToFind;
osg::Group* mFoundNode;
private:
NodeMap mMap;
};
/// Copy the skeleton-space matrix of a "source" bone to a "dest" bone (the bone that the callback is attached to).
/// Must be set on a Bone.
/// Copy the matrix of a "source" node to a "dest" node (the node that the callback is attached to).
/// Must be set on a MatrixTransform.
class CopyController : public osg::NodeCallback
{
public:
CopyController(osgAnimation::Bone* copyFrom)
CopyController(osg::MatrixTransform* copyFrom)
: mCopyFrom(copyFrom)
{
}
@ -66,22 +65,21 @@ namespace SceneUtil
if (mCopyFrom)
{
bone->setMatrix(mCopyFrom->getMatrix());
bone->setMatrixInSkeletonSpace(mCopyFrom->getMatrixInSkeletonSpace());
}
traverse(node, nv);
}
private:
const osgAnimation::Bone* mCopyFrom;
const osg::MatrixTransform* mCopyFrom;
};
class AddCopyControllerVisitor : public osg::NodeVisitor
{
public:
AddCopyControllerVisitor(const osgAnimation::BoneMap& boneMap)
AddCopyControllerVisitor(const NodeMapVisitor::NodeMap& boneMap)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mBoneMap(boneMap)
, mNodeMap(boneMap)
{
}
@ -89,19 +87,23 @@ namespace SceneUtil
{
if (osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&node))
{
osgAnimation::BoneMap::const_iterator found = mBoneMap.find(bone->getName());
if (found != mBoneMap.end())
NodeMapVisitor::NodeMap::const_iterator found = mNodeMap.find(bone->getName());
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->addUpdateCallback(old);
}
}
}
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
// copying is kinda cheap though, so don't bother for now
class FilterVisitor : public osg::NodeVisitor
{
public:
@ -137,13 +139,12 @@ namespace SceneUtil
{
if (osgAnimation::Skeleton* skel = dynamic_cast<osgAnimation::Skeleton*>(toAttach.get()))
{
osgAnimation::Skeleton* masterSkel = dynamic_cast<osgAnimation::Skeleton*>(master);
osgAnimation::BoneMapVisitor boneMapVisitor;
masterSkel->accept(boneMapVisitor);
NodeMapVisitor nodeMapVisitor;
master->accept(nodeMapVisitor);
// 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
AddCopyControllerVisitor visitor(boneMapVisitor.getBoneMap());
AddCopyControllerVisitor visitor(nodeMapVisitor.getNodeMap());
toAttach->accept(visitor);
FilterVisitor filterVisitor(filter);

@ -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 &copy, const osg::CopyOp &copyop)
: 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);
}
}

@ -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

@ -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;
}
}

@ -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

@ -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…
Cancel
Save