#include "lightmanager.hpp" #include #include #include #include #include #include #include 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); } }; // 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;igetNodePath().size(); ++i) { if (LightManager* lightManager = dynamic_cast(nv->getNodePath()[i])) { mLightManager = lightManager; break; } } if (!mLightManager) throw std::runtime_error("can't find parent LightManager"); } mLightManager->addLight(static_cast(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(node); lightManager->update(); traverse(node, nv); } }; LightManager::LightManager() : mLightsInViewSpace(false) , mStartLight(0) { setUpdateCallback(new LightManagerUpdateCallback); } LightManager::LightManager(const LightManager ©, const osg::CopyOp ©op) : osg::Group(copy, copyop) , mLightsInViewSpace(false) , mStartLight(copy.mStartLight) { } void LightManager::update() { mLightsInViewSpace = false; mLights.clear(); mStateSetCache.clear(); } 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::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 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; isecond; else { osg::ref_ptr stateset (new osg::StateSet); for (unsigned int i=0; igetLight(); osg::ref_ptr clonedLight = new LightStateAttribute(*light, osg::CopyOp::DEEP_COPY_ALL); clonedLight->setPosition(mLights[lightIndex].mWorldMatrix.preMult(light->getPosition())); clonedLight->setLightNum(i+mStartLight); // don't use setAttributeAndModes, that does not support light indices! stateset->setAttribute(clonedLight, osg::StateAttribute::ON); stateset->setAssociatedModes(clonedLight, osg::StateAttribute::ON); //stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); } mStateSetCache.insert(std::make_pair(hash, stateset)); return stateset; } } const std::vector& LightManager::getLights() const { return mLights; } void LightManager::setStartLight(int start) { mStartLight = start; } int LightManager::getStartLight() const { return mStartLight; } LightSource::LightSource() : mRadius(0.f) { setUpdateCallback(new CollectLightCallback); } void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv) { osgUtil::CullVisitor* cv = static_cast(nv); if (!mLightManager) { for (unsigned int i=0;igetNodePath().size(); ++i) { if (LightManager* lightManager = dynamic_cast(nv->getNodePath()[i])) { mLightManager = lightManager; break; } } if (!mLightManager) return; } mLightManager->prepareForCamera(cv->getCurrentCamera()); // Possible optimizations: // - cull list of lights by the camera frustum // - organize lights in a quad tree const std::vector& lights = mLightManager->getLights(); if (lights.size()) { static std::map > statesets; std::map >::iterator found = statesets.find(node); osg::ref_ptr stateset; if (found != statesets.end()) { stateset = found->second; } else{ // we do the intersections in view space osg::BoundingSphere nodeBound = node->getBound(); osg::Matrixf mat = *cv->getModelViewMatrix(); transformBoundingSphere(mat, nodeBound); std::vector lightList; for (unsigned int i=0; i (8 - mLightManager->getStartLight()); if (lightList.size() > maxLights) { //std::cerr << "More than 8 lights!" << std::endl; // TODO: sort lights by certain criteria while (lightList.size() > maxLights) lightList.pop_back(); } stateset = mLightManager->getLightListStateSet(lightList); statesets[node] = stateset; } if (stateset) cv->pushStateSet(stateset); traverse(node, nv); if (stateset) cv->popStateSet(); } else traverse(node, nv); } }