#include "OgreOcclusionCullingSceneTraverser.h" #include "OgreMath.h" #include "OgreIteratorWrappers.h" #include "OgreCamera.h" #include "OgreHardwareOcclusionQuery.h" //#include "OgreWireBoundingBox.h" #include "OgreSolidHalfBoundingBox.h" #include namespace Ogre { //----------------------------------------------------------------------- OcclusionCullingSceneTraverser::OcclusionCullingSceneTraverser(SceneManager *sm, RenderSystem *rsys): mFrameId(1), mDistanceQueue(NULL), mVisibilityThreshold(0), mCurrentTestIdx(0), mQueryMode(MODE_RENDER), mNumSceneNodes(0), mCurrentAlgorithm(RENDER_COHERENT), mNumTraversedNodes(0), mNumQueryCulledNodes(0), mNumFrustumCulledNodes(0), mNumRenderedGeometry(0), mSceneManager(sm), mRenderSystem(rsys) { mHalfBoundingBox[0] = mHalfBoundingBox[1] = 0; } //----------------------------------------------------------------------- OcclusionCullingSceneTraverser::~OcclusionCullingSceneTraverser() { if(mHalfBoundingBox[0]) delete mHalfBoundingBox[0]; if(mHalfBoundingBox[1]) delete mHalfBoundingBox[1]; deleteQueries(); //SceneManager::~SceneManager(); } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderScene( Camera *cam, SceneNode *root ) { mNumTraversedNodes = 0; mNumQueryCulledNodes = 0; mNumFrustumCulledNodes = 0; mNumRenderedGeometry = 0; mDistanceQueue = new PriorityQueue(myless(cam)); mDistanceQueue->push(root); mCurrentTestIdx = 0; mCurrentAlgorithm = RENDER_CULL_FRUSTUM; switch(mCurrentAlgorithm) { case RENDER_CULL_FRUSTUM: renderCullFrustum(cam); break; case RENDER_STOP_AND_WAIT: renderStopAndWait(cam); break; case RENDER_COHERENT: renderCoherentWithQueue(cam); break; default: renderCullFrustum(cam); break; } delete mDistanceQueue; mFrameId ++; } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderCoherentWithQueue(Camera *cam) { 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(cam, node); } else { mNumQueryCulledNodes ++; } } //-- PART 2: hierarchical traversal if(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); if(cam->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(cam, node); } } else { mNumFrustumCulledNodes ++; } } } } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderCullFrustum(Camera *cam) { while(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); // interesting for visualization purpose node->setNodeVisible(false); if(!cam->isVisible(node->_getWorldAABB())) { // update node's visited flag node->setLastVisited(mFrameId); node->setNodeVisible(true); traverseNode(cam, node); } //else if(cam->isVisible(node->_getWorldAABB())) { mNumQueryCulledNodes ++; }else { mNumFrustumCulledNodes ++; } } } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderStopAndWait(Camera *cam) { while(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); // interesting for the visualization node->setNodeVisible(false); node->setLastVisited(mFrameId); if(cam->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(cam, node); } else { mNumQueryCulledNodes ++; } } else { mNumFrustumCulledNodes ++; } } } //----------------------------------------------------------------------- HardwareOcclusionQuery *OcclusionCullingSceneTraverser::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(); renderBoundingBox(node); query->endOcclusionQuery(); return query; } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::setRenderingMode( int mode ) { // avoid unnecessary state changes if(mode != mQueryMode) { bool enabled = (mode == MODE_RENDER); mRenderSystem->_setColourBufferWriteEnabled(enabled, enabled, enabled, enabled); mRenderSystem->_setDepthBufferWriteEnabled(enabled); mRenderSystem->setLightingEnabled(enabled); mQueryMode = mode; } } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::traverseNode( Camera *cam, SceneNode *node ) { mNumTraversedNodes ++; if(node->numAttachedObjects() > 0) { renderSceneNode(cam, 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 OcclusionCullingSceneTraverser::renderSceneNode( Camera *cam, SceneNode *node ) { setRenderingMode(MODE_RENDER); //HACK (too slow) mSceneManager->_renderSceneNode(cam, node); //MessageBox( NULL, "myplugin registered", "this is my plugin", MB_OK | MB_ICONERROR | MB_TASKMODAL); } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::preprocess( void ) { //-- initialise occlusion queries. deleteQueries(); for(unsigned int i=0; i < mNumSceneNodes; i++) { mOcclusionQueries.push_back(mRenderSystem->createHardwareOcclusionQuery()); } } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::setSceneManager( SceneManager *sm ) { mSceneManager = sm; } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::setRenderSystem( RenderSystem *rsys ) { mRenderSystem = rsys; } //----------------------------------------------------------------------- /*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 OcclusionCullingSceneTraverser::isLeaf( SceneNode *node ) { return (node->numChildren() == 0); } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::pullUpVisibility( SceneNode *node ) { while(node && !node->isNodeVisible()) { node->setNodeVisible(true); node = static_cast(node->getParent()); } } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::deleteQueries( void ) { for(unsigned int i=0; i < mNumSceneNodes; i++) delete mOcclusionQueries[i]; mOcclusionQueries.clear(); } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderBoundingBox( SceneNode *node ) { // Render two halfes of the bounding box (using triangle fans) for(int half = 0; half < 2; half ++) { SolidHalfBoundingBox *box = getSolidHalfBoundingBox(half); box->setupBoundingBox(node->_getWorldAABB()); static RenderOperation ro; mSceneManager->useRenderableViewProjMode(box); box->getRenderOperation(ro); ro.srcRenderable = box; mRenderSystem->_render(ro); //mSceneManager->renderSingleObject(getSolidHalfBoundingBox(half), // getSolidHalfBoundingBox(half)->getTechnique()->getPass(0), false); } } //----------------------------------------------------------------------- SolidHalfBoundingBox *OcclusionCullingSceneTraverser::getSolidHalfBoundingBox( int half ) { if(!mHalfBoundingBox[half]) mHalfBoundingBox[half] = new SolidHalfBoundingBox(half == 1); return mHalfBoundingBox[half]; } //----------------------------------------------------------------------- bool OcclusionCullingSceneTraverser::setOption( const String & key, const void * val ) { if ( key == "Algorithm" ) { mCurrentAlgorithm = * static_cast < const int * > ( val ); return true; } if ( key == "Threshold" ) { mVisibilityThreshold = * static_cast < const int * > ( val ); return true; } return false; } //----------------------------------------------------------------------- bool OcclusionCullingSceneTraverser::getOption( const String & key, void *val ) { if ( key == "Algorithm" ) { * static_cast < int * > ( val ) = mCurrentAlgorithm; return true; } if ( key == "Threshold" ) { * static_cast < unsigned 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 false; } //----------------------------------------------------------------------- bool OcclusionCullingSceneTraverser::getOptionKeys( StringVector & 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; } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::setNumSceneNodes(int num) { mNumSceneNodes = num; } }