#include "OgreOcclusionCullingSceneManager.h" #include "OgreMath.h" #include "OgreIteratorWrappers.h" #include "OgreRenderSystem.h" #include "OgreCamera.h" #include "OgreHardwareOcclusionQuery.h" //#include "OgreWireBoundingBox.h" #include "OgreSolidHalfBoundingBox.h" #include namespace Ogre { //----------------------------------------------------------------------- OcclusionCullingSceneManager::OcclusionCullingSceneManager(): mFrameId(1), mDistanceQueue(NULL), mVisibilityThreshold(0), mCurrentTestIdx(0), mQueryMode(MODE_RENDER), mNumSceneNodes(0), mCurrentAlgorithm(RENDER_COHERENT), mNumTraversedNodes(0), mNumQueryCulledNodes(0), mNumFrustumCulledNodes(0), mNumRenderedGeometry(0) { mHalfBoundingBox[0] = mHalfBoundingBox[1] = 0; } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::_findVisibleObjects(Camera* cam, bool onlyShadowCasters) { // empty because we have to find in _renderVisibleObjects } //----------------------------------------------------------------------- OcclusionCullingSceneManager::~OcclusionCullingSceneManager() { if(mHalfBoundingBox[0]) delete mHalfBoundingBox[0]; if(mHalfBoundingBox[1]) delete mHalfBoundingBox[1]; deleteQueries(); //SceneManager::~SceneManager(); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::_renderVisibleObjects(void) { mNumTraversedNodes = 0; mNumQueryCulledNodes = 0; mNumFrustumCulledNodes = 0; mNumRenderedGeometry = 0; mDistanceQueue = new PriorityQueue(myless(mCameraInProgress)); mDistanceQueue->push(mSceneRoot); mCurrentTestIdx = 0; //renderZPass(); switch(mCurrentAlgorithm) { case RENDER_CULL_FRUSTUM: renderCullFrustum(); break; case RENDER_STOP_AND_WAIT: renderStopAndWait(); break; case RENDER_COHERENT: renderCoherentWithQueue(); break; default: renderCullFrustum(); break; } delete mDistanceQueue; mFrameId ++; } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderZPass() { traverseNode(mSceneRoot); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderCoherentWithQueue() { QueryQueue queryQueue; //-- PART 1: process finished occlusion queries while(!mDistanceQueue->empty() || !queryQueue.empty()) { while(!queryQueue.empty() && ((queryQueue.front().second)->resultAvailable() || mDistanceQueue->empty())) { SceneNode *node = queryQueue.front().first; HardwareOcclusionQuery *query = queryQueue.front().second; queryQueue.pop(); // wait until result available unsigned int visiblePixels; query->pullOcclusionQuery(&visiblePixels); if(visiblePixels > mVisibilityThreshold) { pullUpVisibility(node); traverseNode(node); } else { mNumQueryCulledNodes ++; } } //-- PART 2: hierarchical traversal if(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); if(mCameraInProgress->isVisible(node->_getWorldAABB())) { // identify previously visible nodes bool wasVisible = node->isNodeVisible() && (node->lastVisited() == mFrameId - 1); // identify nodes that we cannot skip queries for bool leafOrWasInvisible = !wasVisible || isLeaf(node); // reset node's visibility classification node->setNodeVisible(false); // update node's visited flag node->setLastVisited(mFrameId); // skip testing previously visible interior nodes if(leafOrWasInvisible) { HardwareOcclusionQuery *query = issueOcclusionQuery(node, wasVisible); queryQueue.push(query_pair(node, query)); } // always traverse a node if it was visible if(wasVisible) { traverseNode(node); } } else { mNumFrustumCulledNodes ++; } } } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderCullFrustum() { while(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); // interesting for visualization purpose node->setNodeVisible(false); if(mCameraInProgress->isVisible(node->_getWorldAABB())) { // update node's visited flag node->setLastVisited(mFrameId); node->setNodeVisible(true); traverseNode(node); } else { mNumFrustumCulledNodes ++; } } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderStopAndWait() { while(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); // interesting for the visualization node->setNodeVisible(false); node->setLastVisited(mFrameId); if(mCameraInProgress->isVisible(node->_getWorldAABB())) { HardwareOcclusionQuery *query = issueOcclusionQuery(node, false); unsigned int visiblePixels; // wait if result not available query->pullOcclusionQuery(&visiblePixels); // node visible if(visiblePixels > mVisibilityThreshold) { traverseNode(node); } else { mNumQueryCulledNodes ++; } } else { mNumFrustumCulledNodes ++; } } } //----------------------------------------------------------------------- HardwareOcclusionQuery *OcclusionCullingSceneManager::issueOcclusionQuery(SceneNode *node, bool wasVisible) { // change state so the bounding box gets not actually rendered on the screen setRenderingMode(MODE_QUERY); // get next available test id HardwareOcclusionQuery *query = mOcclusionQueries[mCurrentTestIdx++]; query->beginOcclusionQuery(); /* static RenderOperation ro; useRenderableViewProjMode(&box); box.getRenderOperation(ro); ro.srcRenderable = &box; mDestRenderSystem->_render(ro); */ renderBoundingBox(node); query->endOcclusionQuery(); return query; } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::setRenderingMode(int mode) { // avoid unnecessary state changes if(mode != mQueryMode) { bool enabled = (mode == MODE_RENDER); mDestRenderSystem->_setColourBufferWriteEnabled(enabled, enabled, enabled, enabled); mDestRenderSystem->_setDepthBufferWriteEnabled(enabled); mDestRenderSystem->setLightingEnabled(enabled); mQueryMode = mode; } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::traverseNode(SceneNode *node) { mNumTraversedNodes ++; if(node->numAttachedObjects() > 0) { render(node); } // internal node: add children to priority queue for further processing Node::ChildNodeIterator it = node->getChildIterator(); while (it.hasMoreElements()) { SceneNode* sceneChild = static_cast(it.getNext()); mDistanceQueue->push(sceneChild); } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::render(SceneNode *node) { setRenderingMode(MODE_RENDER); //HACK (too slow) node->_findVisibleObjects(mCameraInProgress, getRenderQueue(), false, mDisplayNodes, false); SceneManager::_renderVisibleObjects(); getRenderQueue()->clear(); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::_updateSceneGraph(Camera* cam) { unsigned int numnodes = countSceneNodes(mSceneRoot); //-- initialise occlusion queries. if(numnodes != mNumSceneNodes) { deleteQueries(); mNumSceneNodes = numnodes; for(unsigned int i=0; i < mNumSceneNodes; i++) { mOcclusionQueries.push_back(mDestRenderSystem->createHardwareOcclusionQuery()); } } SceneManager::_updateSceneGraph(cam); } //----------------------------------------------------------------------- unsigned int OcclusionCullingSceneManager::countSceneNodes(SceneNode *node) { unsigned int result = 1; Node::ChildNodeIterator it = node->getChildIterator(); while (it.hasMoreElements()) { SceneNode* sceneChild = static_cast(it.getNext()); result += countSceneNodes(sceneChild); } return result; } //----------------------------------------------------------------------- bool OcclusionCullingSceneManager::isLeaf(SceneNode *node) { return (node->numChildren() == 0); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::pullUpVisibility(SceneNode *node) { while(!node->isNodeVisible()) { node->setNodeVisible(true); if(node != mSceneRoot) node = static_cast(node->getParent()); } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::deleteQueries() { for(unsigned int i=0; i < mNumSceneNodes; i++) delete mOcclusionQueries[i]; mOcclusionQueries.clear(); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderBoundingBox(SceneNode *node) { // Render two halfes of the bounding box (using triangle fans) for(int half=0; half < 2; half ++) { getSolidHalfBoundingBox(half)->setupBoundingBox(node->_getWorldAABB()); SceneManager::renderSingleObject(getSolidHalfBoundingBox(half), getSolidHalfBoundingBox(half)->getTechnique()->getPass(0), false); } } //----------------------------------------------------------------------- SolidHalfBoundingBox *OcclusionCullingSceneManager::getSolidHalfBoundingBox(int half) { if(!mHalfBoundingBox[half]) mHalfBoundingBox[half] = new SolidHalfBoundingBox(half == 1); return mHalfBoundingBox[half]; } //----------------------------------------------------------------------- bool OcclusionCullingSceneManager::setOption( const String & key, const void * val ) { if ( key == "Algorithm" ) { mCurrentAlgorithm = * static_cast < const int * > ( val ); return true; } if ( key == "Threshold" ) { mCurrentAlgorithm = * static_cast < const int * > ( val ); return true; } return SceneManager::setOption( key, val ); } //----------------------------------------------------------------------- bool OcclusionCullingSceneManager::getOption( const String & key, void *val ) { if ( key == "Algorithm" ) { * static_cast < int * > ( val ) = mCurrentAlgorithm; return true; } if ( key == "Threshold" ) { * static_cast < int * > ( val ) = mVisibilityThreshold; return true; } if ( key == "NumSceneNodes" ) { * static_cast < unsigned int * > ( val ) = mNumSceneNodes; return true; } if ( key == "NumTraversedNodes" ) { * static_cast < unsigned int * > ( val ) = mNumTraversedNodes; return true; } if ( key == "NumQueryCulledNodes" ) { * static_cast < unsigned int * > ( val ) = mNumQueryCulledNodes; return true; } if ( key == "NumFrustumCulledNodes" ) { * static_cast < unsigned int * > ( val ) = mNumFrustumCulledNodes; return true; } return SceneManager::getOption( key, val ); } //----------------------------------------------------------------------- bool OcclusionCullingSceneManager::getOptionValues( const String & key, StringVector &refValueList ) { return SceneManager::getOptionValues( key, refValueList ); } //----------------------------------------------------------------------- bool OcclusionCullingSceneManager::getOptionKeys( StringVector & refKeys ) { SceneManager::getOptionKeys( refKeys ); refKeys.push_back( "Algorithm" ); refKeys.push_back( "Threshold" ); refKeys.push_back( "NumSceneNodes" ); refKeys.push_back( "NumTraversedNodes" ); refKeys.push_back( "NumQueryCulledNodes" ); refKeys.push_back( "NumFrustumCulledNodes" ); //refKeys.push_back( "mNumRenderedGeometry" ); return true; } }