#include "OgreOcclusionCullingSceneTraverser.h" #include "OgreMath.h" #include "OgreIteratorWrappers.h" #include "OgreCamera.h" #include "OgreHardwareOcclusionQuery.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), mNumQueries(0), mNumTraversedNodes(0), mNumQueryCulledNodes(0), mNumFrustumCulledNodes(0), mNumRenderedNodes(0),mNumRenderedGeometry(0), mSceneManager(sm), mRenderSystem(rsys), mSceneRoot(NULL) { mHalfBoundingBox[0] = mHalfBoundingBox[1] = 0; } //----------------------------------------------------------------------- OcclusionCullingSceneTraverser::~OcclusionCullingSceneTraverser() { if(mHalfBoundingBox[0]) delete mHalfBoundingBox[0]; if(mHalfBoundingBox[1]) delete mHalfBoundingBox[1]; deleteQueries(); if(mDistanceQueue) delete mDistanceQueue; //SceneManager::~SceneManager(); } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderScene( Camera *cam ) { mNumTraversedNodes = 0; mNumQueryCulledNodes = 0; mNumFrustumCulledNodes = 0; mNumRenderedGeometry = 0; mNumRenderedNodes = 0; mCurrentTestIdx = 0; initDistanceQueue(cam); 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; } 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(); bool intersects = false; //TODO: Isvisible also checked inside scenenode::findvisibleobjects if(!cam->isVisible(node->_getWorldAABB(), intersects)) { mNumFrustumCulledNodes ++; continue; } // if intersects near plane => skip occlusion query because wrong results possible if(intersects) { // update node's visited flag node->setLastVisited(mFrameId); pullUpVisibility(node); traverseNode(cam, node); continue; } // identify previously visible nodes bool wasVisible = node->isNodeVisible() && (node->lastVisited() == mFrameId - 1); // identify nodes that we cannot skip queries for bool mustQuery= !wasVisible || (node->numAttachedObjects() > 0) || isLeaf(node); // reset node's visibility classification node->setNodeVisible(false); // update node's visited flag node->setLastVisited(mFrameId); // skip testing previously visible interior nodes without geometry if(mustQuery) { HardwareOcclusionQuery *query = issueOcclusionQuery(&node->_getWorldAABB(), wasVisible); queryQueue.push(QueryPair(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); //TODO: IsVisible also checked inside scenenode::_findvisibleobjects if(!cam->isVisible(node->_getWorldAABB())) { mNumFrustumCulledNodes ++; continue; } // update node's visited flag node->setLastVisited(mFrameId); node->setNodeVisible(true); traverseNode(cam, node); } } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderStopAndWait(Camera *cam) { while(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); // interesting for the visualization node->setNodeVisible(false); node->setLastVisited(mFrameId); bool intersects = false; //TODO: Isvisible also checked inside scenenode::findvisibleobjects if(!cam->isVisible(node->_getWorldAABB(), intersects)) { mNumFrustumCulledNodes ++; continue; } // if intersects near plane => skip occlusion query because wrong results possible if(intersects) { node->setNodeVisible(true); traverseNode(cam, node); continue; } HardwareOcclusionQuery *query = issueOcclusionQuery(&node->_getWorldAABB(), false); unsigned int visiblePixels; // wait if result not available query->pullOcclusionQuery(&visiblePixels); // node visible if(visiblePixels > mVisibilityThreshold) { node->setNodeVisible(true); traverseNode(cam, node); } else { mNumQueryCulledNodes ++; } } } //----------------------------------------------------------------------- HardwareOcclusionQuery *OcclusionCullingSceneTraverser::issueOcclusionQuery( AxisAlignedBox *box, bool wasVisible ) { // change state so the bounding box gets not actually rendered on the screen // TODO: in rendervisibleobjects, the rendermode is changed by ogre itself => change this!! //setRenderingMode(MODE_QUERY); //mRenderSystem->_setRasterisationMode(SDL_SOLID); setRenderingMode(MODE_RENDER); // get next available test id HardwareOcclusionQuery *query = getNextOcclusionQuery(); //-- the actual query test query->beginOcclusionQuery(); renderBoundingBox(box); query->endOcclusionQuery(); /*mRenderSystem->_setRasterisationMode(SDL_WIREFRAME); setRenderingMode(MODE_RENDER); renderBoundingBox(box); mRenderSystem->_setRasterisationMode(SDL_SOLID);*/ return query; } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::setRenderingMode( int mode ) { // avoid unnecessary state changes if(mode != mQueryMode) { mQueryMode = mode; bool enabled = (mode == MODE_RENDER); //if(mode == MODE_RENDER) return; /* mRenderSystem->_setRasterisationMode(SDL_SOLID); mRenderSystem->_setSurfaceParams(ColourValue(0.9, 0.2, 0.2), ColourValue(0.2, 0.2, 0.2), ColourValue(0.2, 0.2, 0.2), ColourValue(0, 0, 0), Real(100)); mRenderSystem->_setDepthBufferCheckEnabled(false); mRenderSystem->_setAlphaRejectSettings(CMPF_ALWAYS_PASS, 255); */ mRenderSystem->_setColourBufferWriteEnabled(enabled, enabled, enabled, enabled); mRenderSystem->_setDepthBufferWriteEnabled(enabled); //mRenderSystem->setLightingEnabled(enabled); } } //----------------------------------------------------------------------- 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 ) { //TODO: does not do any changes //setRenderingMode(MODE_RENDER); if(node->lastRendered() != node->lastVisited()) { node->setLastRendered(node->lastVisited()); mNumRenderedNodes ++; mSceneManager->_renderSceneNode(cam, node); } } //----------------------------------------------------------------------- /* void OcclusionCullingSceneTraverser::preprocess( void ) { //-- initialise occlusion queries. deleteQueries(); for(unsigned int i=0; i < mNumQueries; 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 = node->getParentSceneNode(); } } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::deleteQueries( void ) { for(unsigned int i=0; i < (unsigned int)mOcclusionQueries.size(); ++i) delete mOcclusionQueries[i]; mOcclusionQueries.clear(); } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::renderBoundingBox( AxisAlignedBox *box ) { // Render two halfes of the bounding box (using triangle fans) for(int half = 0; half < 2; half ++) { static Matrix4 xform[256]; //TODO: this should be full bounding box SolidHalfBoundingBox *halfbox = getSolidHalfBoundingBox(half); halfbox->setupBoundingBox(*box); /*mRenderSystem->_setWorldMatrix(Matrix4::IDENTITY); halfbox->getWorldTransforms(xform); int numMatrices = halfbox->getNumWorldTransforms(); if (numMatrices > 1) { mRenderSystem->_setWorldMatrices(xform, numMatrices); } else { mRenderSystem->_setWorldMatrix(*xform); } mRenderSystem->setClipPlanes(halfbox->getClipPlanes()); static RenderOperation ro; mSceneManager->useRenderableViewProjMode(halfbox); halfbox->getRenderOperation(ro); ro.srcRenderable = halfbox; mRenderSystem->_render(ro);*/ mSceneManager->myrenderSingleObject(getSolidHalfBoundingBox(half), getSolidHalfBoundingBox(half)->getTechnique()->getPass(0), true); } } //----------------------------------------------------------------------- SolidHalfBoundingBox *OcclusionCullingSceneTraverser::getSolidHalfBoundingBox( int half ) { if(!mHalfBoundingBox[half]) mHalfBoundingBox[half] = new SolidHalfBoundingBox(half == 1); return mHalfBoundingBox[half]; } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::setNumSceneNodes(int num) { mNumSceneNodes = num; } //----------------------------------------------------------------------- /*void OcclusionCullingSceneTraverser::setNumQueries(int num) { mNumQueries = num; }*/ //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::setSceneRoot(SceneNode *root) { mSceneRoot = root; } //----------------------------------------------------------------------- void OcclusionCullingSceneTraverser::initDistanceQueue(Camera *cam) { mDistanceQueue = new PriorityQueue(myless(cam)); mDistanceQueue->push(mSceneRoot); } //----------------------------------------------------------------------- HardwareOcclusionQuery *OcclusionCullingSceneTraverser::getNextOcclusionQuery(void) { if(mCurrentTestIdx == mOcclusionQueries.size()) { mOcclusionQueries.push_back(mRenderSystem->createHardwareOcclusionQuery()); } return mOcclusionQueries[mCurrentTestIdx ++]; } //----------------------------------------------------------------------- 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; } if ( key == "NumRenderedNodes" ) { * static_cast < unsigned int * > ( val ) = mNumRenderedNodes; 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; } }