#include "OgreOcclusionCullingOctreeSceneTraverser.h" #include "OgreOcclusionCullingOctreeSceneManager.h" #include "OgreHardwareOcclusionQuery.h" #include namespace Ogre { //----------------------------------------------------------------------- OcclusionCullingOctreeSceneTraverser::OcclusionCullingOctreeSceneTraverser(SceneManager *sm, RenderSystem *rsys): OcclusionCullingSceneTraverser(sm, rsys), mOctreeSceneRoot(0), mOctreeDistanceQueue(0) { } //----------------------------------------------------------------------- OcclusionCullingOctreeSceneTraverser::~OcclusionCullingOctreeSceneTraverser() { if(mOctreeDistanceQueue) delete mOctreeDistanceQueue; } //----------------------------------------------------------------------- void OcclusionCullingOctreeSceneTraverser::traverseOctant(Camera *cam, Octree *octant) { mNumTraversedNodes ++; // if we come across some renderable geometry => render it renderOctant(cam, octant); for(int i=0; i<8; i++) { Octree *nextChild = octant->mChildren[(i & 4) >> 2][(i & 2) >> 1][i & 1]; if(nextChild) { mOctreeDistanceQueue->push(nextChild); } } } //----------------------------------------------------------------------- void OcclusionCullingOctreeSceneTraverser::renderCullFrustum(Camera *cam) { AxisAlignedBox box; while(!mOctreeDistanceQueue->empty()) { Octree *octant = mOctreeDistanceQueue->top(); mOctreeDistanceQueue->pop(); // interesting for visualization purpose octant->setOctreeVisible(false); octant->_getCullBounds(&box); if(!cam->isVisible(box)) { mNumFrustumCulledNodes ++; continue; } mRenderSystem->_setRasterisationMode(SDL_WIREFRAME); renderBoundingBox(&box); mRenderSystem->_setRasterisationMode(SDL_SOLID); // update node's visited flag octant->setLastVisited(mFrameId); octant->setOctreeVisible(true); traverseOctant(cam, octant); } } //----------------------------------------------------------------------- /** Renders the scene with the hierarchical stop and wait algorithm. */ void OcclusionCullingOctreeSceneTraverser::renderStopAndWait( Camera *cam ) { AxisAlignedBox box; while(!mOctreeDistanceQueue->empty()) { Octree *octant = mOctreeDistanceQueue->top(); mOctreeDistanceQueue->pop(); // interesting for visualization purpose octant->setOctreeVisible(false); octant->setLastVisited(mFrameId); //TODO: Isvisible also checked inside scenenode::findvisibleobjects octant->_getCullBounds(&box); bool intersects = false; if(!cam->isVisible(box, intersects)) { mNumFrustumCulledNodes ++; continue; } //if intersects near plane => skip occlusion query because wrong results possible if(intersects) { octant->setOctreeVisible(true); traverseOctant(cam, octant); continue; } HardwareOcclusionQuery *query = issueOcclusionQuery(&box, false); unsigned int visiblePixels; // wait if result not available query->pullOcclusionQuery(&visiblePixels); //char msg[100]; sprintf(msg, "visible pixels: %d\n", visiblePixels); //OutputDebugString(msg); // node visible if(visiblePixels > mVisibilityThreshold) { traverseOctant(cam, octant); } else { mNumQueryCulledNodes ++; } } } //----------------------------------------------------------------------- /** Renders the scene with the coherent hierarchical algorithm and the query queye. */ void OcclusionCullingOctreeSceneTraverser::renderCoherentWithQueue( Camera *cam ) { OctreeQueryQueue queryQueue; AxisAlignedBox box; //-- PART 1: process finished occlusion queries while(!mOctreeDistanceQueue->empty() || !queryQueue.empty()) { while(!queryQueue.empty() && ((queryQueue.front().second)->resultAvailable() || mOctreeDistanceQueue->empty())) { Octree *octant = queryQueue.front().first; HardwareOcclusionQuery *query = queryQueue.front().second; queryQueue.pop(); // wait until result available unsigned int visiblePixels; query->pullOcclusionQuery(&visiblePixels); if(visiblePixels > mVisibilityThreshold) { pullUpVisibility(octant); traverseOctant(cam, octant); } else { mNumQueryCulledNodes ++; } } //-- PART 2: hierarchical traversal if(!mOctreeDistanceQueue->empty()) { Octree *octant = mOctreeDistanceQueue->top(); mOctreeDistanceQueue->pop(); octant->_getCullBounds(&box); bool intersects = false; //TODO: Isvisible also checked inside scenenode::findvisibleobjects if(!cam->isVisible(box, intersects)) { mNumFrustumCulledNodes ++; continue; } // if intersects near plane => skip occlusion query because wrong results possible if(intersects) { // update octant's visited flag octant->setLastVisited(mFrameId); pullUpVisibility(octant); traverseOctant(cam, octant); continue; } // identify previously visible nodes bool wasVisible = octant->isOctreeVisible() && (octant->lastVisited() == mFrameId - 1); // identify nodes that we cannot skip queries for bool mustQuery = !wasVisible || (octant->numNodes() > 0) || isLeaf(octant); // reset node's visibility classification octant->setOctreeVisible(false); // update node's visited flag octant->setLastVisited(mFrameId); // skip testing previously visible interior nodes if(mustQuery) { HardwareOcclusionQuery *query = issueOcclusionQuery(&box, wasVisible); queryQueue.push(OctreeQueryPair(octant, query)); } // always traverse a node if it was visible if(wasVisible) { traverseOctant(cam, octant); } } } } //----------------------------------------------------------------------- void OcclusionCullingOctreeSceneTraverser::pullUpVisibility( Octree *octant ) { while(octant && !octant->isOctreeVisible()) { octant->setOctreeVisible(true); octant = octant->getParent(); } } //----------------------------------------------------------------------- void OcclusionCullingOctreeSceneTraverser::renderOctant( Camera *cam, Octree *octant ) { if(octant->lastRendered() != mFrameId) { octant->setLastRendered(mFrameId); if(octant->numNodes() == 0) return; //TODO: does nothing useful //setRenderingMode(MODE_RENDER); ((OctreeSceneManager *)mSceneManager)->_renderOctant(cam, octant); mNumRenderedNodes ++; } } //----------------------------------------------------------------------- void OcclusionCullingOctreeSceneTraverser::setSceneRoot( Octree *root ) { mOctreeSceneRoot = root; } //----------------------------------------------------------------------- void OcclusionCullingOctreeSceneTraverser::initDistanceQueue( Camera *cam ) { if(mOctreeDistanceQueue) delete mOctreeDistanceQueue; mOctreeDistanceQueue = new OctreePriorityQueue(octreeless(cam)); mOctreeDistanceQueue->push(mOctreeSceneRoot); } //----------------------------------------------------------------------- bool OcclusionCullingOctreeSceneTraverser::isLeaf(Octree *octant) { for(int i=0; i<8; i++) { if(octant->mChildren[(i & 4) >> 2][(i & 2) >> 1][i & 1]) return false; } return true; } //----------------------------------------------------------------------- void OcclusionCullingOctreeSceneTraverser::setNumOctreeNodes(unsigned int num) { mNumOctreeNodes = num; } //----------------------------------------------------------------------- bool OcclusionCullingOctreeSceneTraverser::getOption( const String & key, void *val ) { if (key == "NumOctreeNodes") { * static_cast < unsigned int * > ( val ) = mNumOctreeNodes; return true; } return OcclusionCullingSceneTraverser::getOption(key, val); } //----------------------------------------------------------------------- bool OcclusionCullingOctreeSceneTraverser::getOptionKeys( StringVector & refKeys ) { refKeys.push_back("NumOctreeNodes"); return OcclusionCullingSceneTraverser::getOptionKeys(refKeys); } }