#include "OgreOcclusionCullingTerrainSceneTraverser.h" #include "OgreOcclusionCullingTerrainSceneManager.h" #include "OgreHardwareOcclusionQuery.h" #include namespace Ogre { //----------------------------------------------------------------------- OcclusionCullingTerrainSceneTraverser::OcclusionCullingTerrainSceneTraverser(SceneManager *sm, RenderSystem *rsys): OcclusionCullingSceneTraverser(sm, rsys), mOctreeSceneRoot(0), mOctreeDistanceQueue(0) { } //----------------------------------------------------------------------- OcclusionCullingTerrainSceneTraverser::~OcclusionCullingTerrainSceneTraverser() { if(mOctreeDistanceQueue) delete mOctreeDistanceQueue; } //----------------------------------------------------------------------- void OcclusionCullingTerrainSceneTraverser::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 OcclusionCullingTerrainSceneTraverser::renderCullFrustum(Camera *cam) { AxisAlignedBox box; while(!mOctreeDistanceQueue->empty()) { Octree *octant = mOctreeDistanceQueue->top(); mOctreeDistanceQueue->pop(); // interesting for visualization purpose //TODO: octree->setNodeVisible(false); octant->_getCullBounds(&box); //if(static_cast(cam)->getVisibility(box) == OctreeCamera::NONE) if(!cam->isVisible(box)) { mNumFrustumCulledNodes ++; continue; } // 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 OcclusionCullingTerrainSceneTraverser::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(vis == OctreeCamera::NONE) 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); // node visible if(visiblePixels > mVisibilityThreshold) { traverseOctant(cam, octant); } else { mNumQueryCulledNodes ++; } } } //----------------------------------------------------------------------- /** Renders the scene with the coherent hierarchical algorithm and the query queye. */ void OcclusionCullingTerrainSceneTraverser::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(cam, 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 node's visited flag octant->setLastVisited(mFrameId); pullUpVisibility(cam, 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 leafOrWasInvisible = !wasVisible || 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(leafOrWasInvisible) { HardwareOcclusionQuery *query = issueOcclusionQuery(&box, wasVisible); queryQueue.push(OctreeQueryPair(octant, query)); } // always traverse a node if it was visible if(wasVisible) { traverseOctant(cam, octant); } else { mNumFrustumCulledNodes ++; } } } } //----------------------------------------------------------------------- void OcclusionCullingTerrainSceneTraverser::pullUpVisibility( Camera *cam, Octree *octant ) { while(octant && !octant->isOctreeVisible()) { // if we come across some renderable geometry => render it renderOctant(cam, octant); octant->setOctreeVisible(true); octant = octant->getParent(); } } //----------------------------------------------------------------------- void OcclusionCullingTerrainSceneTraverser::renderOctant( Camera *cam, Octree *octant ) { //if(octant->numNodes() > 0) if(octant->lastVisited() != octant->lastRendered()) { //setRenderingMode(MODE_RENDER); octant->setLastRendered(octant->lastVisited()); ((OcclusionCullingTerrainSceneManager *)mSceneManager)->_renderOctant(cam, octant); } else OutputDebugString("already rendered"); } //----------------------------------------------------------------------- void OcclusionCullingTerrainSceneTraverser::setSceneRoot(Octree *root) { mOctreeSceneRoot = root; } //----------------------------------------------------------------------- void OcclusionCullingTerrainSceneTraverser::initDistanceQueue(Camera *cam) { if(mOctreeDistanceQueue) delete mOctreeDistanceQueue; mOctreeDistanceQueue = new OctreePriorityQueue(octreeless(cam)); mOctreeDistanceQueue->push(mOctreeSceneRoot); } //----------------------------------------------------------------------- bool OcclusionCullingTerrainSceneTraverser::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 OcclusionCullingTerrainSceneTraverser::setNumOctreeNodes(unsigned int num) { mNumOctreeNodes = num; } //----------------------------------------------------------------------- bool OcclusionCullingTerrainSceneTraverser::getOption( const String & key, void *val ) { if ( key == "NumOctreeNodes" ) { * static_cast < unsigned int * > ( val ) = mNumOctreeNodes; return true; } return OcclusionCullingSceneTraverser::getOption(key, val); } //----------------------------------------------------------------------- bool OcclusionCullingTerrainSceneTraverser::getOptionKeys( StringVector & refKeys ) { refKeys.push_back( "NumOctreeNodes" ); return OcclusionCullingSceneTraverser::getOptionKeys(refKeys); } }