#include "OgreOcclusionCullingSceneManager.h" #include "OgreMath.h" #include "OgreIteratorWrappers.h" #include "OgreRenderSystem.h" #include "OgreCamera.h" #include "OgreHardwareOcclusionQuery.h" //#include "OgreWireBoundingBox.h" #include "OgreSolidHalfBoundingBox.h" #include namespace Ogre { //----------------------------------------------------------------------- OcclusionCullingSceneManager::OcclusionCullingSceneManager(): mFrameId(1), mDistanceQueue(NULL), mVisibilityThreshold(0), mCurrentTestIdx(0), mQueryMode(MODE_RENDER), mNumSceneNodes(0), mAlgorithmType(RENDER_COHERENT) { mHalfBoundingBox[0] = mHalfBoundingBox[1] = 0; } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::_findVisibleObjects(Camera* cam, bool onlyShadowCasters) { // empty because we have to find in _renderVisibleObjects } //----------------------------------------------------------------------- OcclusionCullingSceneManager::~OcclusionCullingSceneManager() { if(mHalfBoundingBox[0]) delete mHalfBoundingBox[0]; if(mHalfBoundingBox[1]) delete mHalfBoundingBox[1]; deleteQueries(); //SceneManager::~SceneManager(); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::_renderVisibleObjects(void) { mDistanceQueue = new PriorityQueue(myless(mCameraInProgress)); mDistanceQueue->push(mSceneRoot); mCurrentTestIdx = 0; //renderZPass(); switch(mAlgorithmType) { case RENDER_CULL_FRUSTUM: renderCullFrustum(); break; case RENDER_STOP_AND_WAIT: renderStopAndWait(); break; case RENDER_COHERENT: renderCoherentWithQueue(); break; default: renderCullFrustum(); break; } delete mDistanceQueue; /*mDestRenderSystem->_setDepthBufferParams(true, false, CMPF_LESS); SceneManager::_renderVisibleObjects(); mDestRenderSystem->_setDepthBufferParams(); mDistanceQueue.push(mSceneRoot); Preprocess(); //ResetQueries(); */ mFrameId ++; } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderZPass() { traverseNode(mSceneRoot); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderCoherentWithQueue() { 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(node); } } //-- PART 2: hierarchical traversal if(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); if(mCameraInProgress->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(node); } } } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderCullFrustum() { while(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); // interesting for visualization purpose node->setNodeVisible(false); if(mCameraInProgress->isVisible(node->_getWorldAABB())) { // update node's visited flag node->setLastVisited(mFrameId); node->setNodeVisible(true); traverseNode(node); } } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderStopAndWait() { while(!mDistanceQueue->empty()) { SceneNode *node = mDistanceQueue->top(); mDistanceQueue->pop(); // interesting for the visualization node->setNodeVisible(false); node->setLastVisited(mFrameId); if(mCameraInProgress->isVisible(node->_getWorldAABB())) { HardwareOcclusionQuery *query = issueOcclusionQuery(node, false); unsigned int visiblePixels; // wait if result not available query->pullOcclusionQuery(&visiblePixels); //char str[100]; sprintf(str, "number: %d, id: %d", (int)visiblePixels, mCurrentTestIdx); //MessageBox( NULL, str, "this is my plugin", MB_OK | MB_ICONERROR | MB_TASKMODAL); // node visible if(visiblePixels > mVisibilityThreshold) { traverseNode(node); } } } } //----------------------------------------------------------------------- HardwareOcclusionQuery *OcclusionCullingSceneManager::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(); /* static RenderOperation ro; // Issue view / projection changes if any useRenderableViewProjMode(&box); box.getRenderOperation(ro); ro.srcRenderable = &box; mDestRenderSystem->_render(ro); */ renderBoundingBox(node); query->endOcclusionQuery(); return query; } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::setRenderingMode(int mode) { // avoid unnecessary state changes if(mode != mQueryMode) { bool enabled = (mode == MODE_RENDER); mDestRenderSystem->_setColourBufferWriteEnabled(enabled, enabled, enabled, enabled); mDestRenderSystem->_setDepthBufferWriteEnabled(enabled); mDestRenderSystem->setLightingEnabled(enabled); mQueryMode = mode; } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::traverseNode(SceneNode *node) { if(isLeaf(node)) { render(node); } else // 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 OcclusionCullingSceneManager::render(SceneNode *node) { //setRenderingMode(MODE_RENDER); //MessageBox( NULL, "harhar", "this is my plugin", MB_OK | MB_ICONERROR | MB_TASKMODAL); //HACK (too slow) node->_findVisibleObjects(mCameraInProgress, getRenderQueue(), false, mDisplayNodes, false); SceneManager::_renderVisibleObjects(); getRenderQueue()->clear(); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::_updateSceneGraph(Camera* cam) { int numnodes = countSceneNodes(mSceneRoot); //-- initialise occlusion queries. if(numnodes != mNumSceneNodes) { deleteQueries(); mNumSceneNodes = numnodes; for(unsigned int i=0; i < mNumSceneNodes; i++) { HardwareOcclusionQuery *hw = mDestRenderSystem->createHardwareOcclusionQuery(); mOcclusionQueries.push_back(hw); } } SceneManager::_updateSceneGraph(cam); } //----------------------------------------------------------------------- int OcclusionCullingSceneManager::countSceneNodes(SceneNode *node) { int num = 1; if(isLeaf(node)) { return num; } Node::ChildNodeIterator it = node->getChildIterator(); while (it.hasMoreElements()) { SceneNode* sceneChild = static_cast(it.getNext()); num += countSceneNodes(sceneChild); } return num; } //----------------------------------------------------------------------- int OcclusionCullingSceneManager::isLeaf(SceneNode *node) { return (node->numChildren() == 0); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::pullUpVisibility(SceneNode *node) { while(!node->isNodeVisible()) { node->setNodeVisible(true); if(node != mSceneRoot) node = static_cast(node->getParent()); } } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::deleteQueries() { for(unsigned int i=0; i < mNumSceneNodes; i++) delete mOcclusionQueries[i]; mOcclusionQueries.clear(); } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::renderBoundingBox(SceneNode *node) { // Render two halfes of the bounding box (using triangle fans) for(int half=0; half < 2; half ++) { getSolidHalfBoundingBox(half)->setupBoundingBox(node->_getWorldAABB()); SceneManager::renderSingleObject(getSolidHalfBoundingBox(half), getSolidHalfBoundingBox(half)->getTechnique()->getPass(0), false); } } //----------------------------------------------------------------------- SolidHalfBoundingBox *OcclusionCullingSceneManager::getSolidHalfBoundingBox(int half) { if(!mHalfBoundingBox[half]) mHalfBoundingBox[half] = new SolidHalfBoundingBox(half == 1); return mHalfBoundingBox[half]; } //----------------------------------------------------------------------- void OcclusionCullingSceneManager::setAlgorithmType(int type) { mAlgorithmType = type; } //----------------------------------------------------------------------- int OcclusionCullingSceneManager::getAlgorithmType() { return mAlgorithmType; } }