#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]; /*char msg[100]; sprintf(msg, "node number: %d %d %d\n", (i & 4) >> 2, (i & 2) >> 1, i & 1); OutputDebugString(msg);*/ 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; } // 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; /*char msg2[100]; Vector3 min = box.getMinimum(); Vector3 max = box.getMaximum(); sprintf(msg2, "culling box: %3.3f %3.3f %3.3f %3.3f %3.3f %3.3f\n", min.x, min.y, min.z, max.x, max.y, max.z); OutputDebugString(msg2);*/ if(!cam->isVisible(box, intersects)) { mNumFrustumCulledNodes ++; continue; } //if intersects near plane => skip occlusion query because wrong results possible if(intersects) { octant->setOctreeVisible(true); // char msg[100]; sprintf(msg, "intersecting\n"); // OutputDebugString(msg); 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, intersects); 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); /* char msg[100]; if(wasVisible) sprintf(msg, "yes, was visible, %d\n", mFrameId); else sprintf(msg, "no was invisible, %d\n", mFrameId); OutputDebugString(msg); */ // 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); } }