//! (internal) Main culling method State::stateResult_t OccRenderer::performCulling(FrameContext & context,Node * rootNode, const RenderParam & rp){ if(mode==MODE_UNCONDITIONED){ // destroy temporal coherence frameNumber++; } frameNumber++; updateNodeInformation(context,rootNode); // ??? // used for statistics unsigned int tests=0; int testsVisible=0; int testsInvisible=0; int occludedGeometryNodes=0; Statistics & statistics = context.getStatistics(); RenderParam childParam = rp + USE_WORLD_MATRIX; const Geometry::Vec3 camPos=context.getCamera()->getWorldOrigin(); // NodeDistancePriorityQueue_F2B distanceQueue(camPos); NodeDistancePriorityQueue_F2B distanceQueue(camPos); { // add root node's children const auto children = getChildNodes(rootNode); for(auto & child : children) { distanceQueue.push(child); } } std::queue<std::pair<Rendering::OcclusionQuery, Node *>> queryQueue; // bool wasIdle=true; int idleCount=0; while ( (!distanceQueue.empty()) || (!queryQueue.empty() ) ) { bool idle=true; while ( !queryQueue.empty() && queryQueue.front().first.isResultAvailable() ) { idle=false; const OcclusionQuery & currentQuery = queryQueue.front().first; unsigned int result = currentQuery.getResult(); Node * node = queryQueue.front().second; queryQueue.pop(); NodeInfo * nodeInfo = getNodeInfo(node); // Node is visible? if (result>0 ) { statistics.pushEvent(Statistics::EVENT_TYPE_END_TEST_VISIBLE,nodeInfo->getActualSubtreeComplexity()); testsVisible++; if (node->isClosed()) { // mark parents as visible Node * n=node; NodeInfo * ni=nodeInfo; while( ni->getVisibleFrameNumber()!=frameNumber){ ni->setVisibleFrameNumber(frameNumber); n=n->getParent(); if(n==nullptr || n==rootNode) break; ni=getNodeInfo(n); } } nodeInfo->setVisibleFrameNumber(frameNumber); // haben wir schon bearbeitet? -> Weiter... if (nodeInfo->getProcessedFrameNumber()==frameNumber) continue; //// if(node->visibleFrameNumber != (frameNumber-1)){ ???? // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); //// } }// Node is fully occluded? else { testsInvisible++; statistics.pushEvent(Statistics::EVENT_TYPE_END_TEST_INVISIBLE, nodeInfo->getActualSubtreeComplexity()); // not processed yet? if (nodeInfo->getProcessedFrameNumber()!=frameNumber) { // TODO: Store and propagate in tree. occludedGeometryNodes+=collectNodesInFrustum<GeometryNode>(node,context.getCamera()->getFrustum()).size(); // mark the node as processed nodeInfo->setProcessedFrameNumber(frameNumber); } // In diesem Frame verdeckt worden? (dann haben wir nicht mit der Bearbeitung gewartet und // die Kinder liegen bereits in der distanceQueue; die sollten jetzt �bersprungen werden) if ( nodeInfo->getVisibleFrameNumber() == (frameNumber-1) ) { if (!node->isClosed()) { const auto children = getChildNodes(node); for(auto & child : children) { getNodeInfo(child)->setProcessedFrameNumber(frameNumber); // TODO: Check if ancestors have to be removed too. } } } } } // ... if ( ! distanceQueue.empty()) { idle=false; Node * node=distanceQueue.top(); NodeInfo * nodeInfo=getNodeInfo(node); distanceQueue.pop(); // If the current node has already been processed, then continue. if (nodeInfo->getProcessedFrameNumber()==frameNumber ) continue; // ||node->isEmpty() // // skip empty subtrees // does never happen (checked in processNode) // if( nodeInfo->getActualSubtreeComplexity() == 0){ // nullBoxes++; // continue; // } // Kamera innerhalb der (um die nearplane vergroesserte) Box? -> immer sichtbar // [2008-01-04_CJ] Bugfix: added nearplane addition; could result in dissapearing scene-parts. Geometry::Box extendedBox=node->getWorldBB(); //node->getGeometryBB(); extendedBox.resizeAbs(context.getCamera()->getNearPlane()); if (extendedBox.contains(camPos)) { nodeInfo->setVisibleFrameNumber(frameNumber); // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); continue; } // !IS_INSIDE_FRUSTUM(node) if ( nodeInfo->getActualFrustumStatus()== Geometry::Frustum::OUTSIDE) continue; // [MODE_OPT_CULLING] For optimal culling, skip all unnecessary tests if(mode==MODE_OPT_CULLING && nodeInfo->getVisibleFrameNumber() == (frameNumber-1)){ nodeInfo->setVisibleFrameNumber(frameNumber); // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); continue; } // if query reasonable // TODO: Something missing here? { // start occlusion test for the current node Rendering::OcclusionQuery query; Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); query.begin(); Rendering::drawFastAbsBox(context.getRenderingContext(), node->getWorldBB()); query.end(); Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); tests++; queryQueue.emplace(std::move(query), node); statistics.pushEvent(Statistics::EVENT_TYPE_START_TEST, nodeInfo->getActualSubtreeComplexity()); } // if the current node was visible in the last frame, // then assume that it is also visible in this frame and process it. if (nodeInfo->getVisibleFrameNumber() == (frameNumber-1) ) { // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); } } // else{ // waiting // } if (idle) { statistics.pushEvent(Statistics::EVENT_TYPE_IDLE, idleCount++); } else { idleCount = 0; } } statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestCounter(), tests); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestVisibleCounter(), testsVisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestInvisibleCounter(), testsInvisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getCulledGeometryNodeCounter(), occludedGeometryNodes); statistics.pushEvent(Statistics::EVENT_TYPE_FRAME_END,0.3); return State::STATE_SKIP_RENDERING; }
//! ---|> [State] State::stateResult_t CHCRenderer::doEnableState(FrameContext & context,Node * rootNode, const RenderParam & rp){ if(rp.getFlag(SKIP_RENDERER)) return State::STATE_SKIPPED; if(debugShowVisible){ std::deque<Node *> nodes; { const auto children = getChildNodes(rootNode); nodes.insert(nodes.end(), children.begin(), children.end()); } while(!nodes.empty()){ Node& node = *nodes.front(); nodes.pop_front(); auto& nodeInfo = getNodeInfo(node); if(nodeInfo.frameNr == frameNr && nodeInfo.visible){ if(node.isClosed()) context.displayNode(&node,rp); else { const auto children = getChildNodes(&node); nodes.insert(nodes.end(), children.begin(), children.end()); } } } }else{ ++frameNr; // used for statistics unsigned int stat_numTests = 0; unsigned int stat_numTestsInvisible = 0; unsigned int stat_numTestsVisible = 0; Statistics & statistics = context.getStatistics(); RenderParam childParam = rp + USE_WORLD_MATRIX; const auto& camera = *context.getCamera(); const Geometry::Vec3 camPos = camera.getWorldOrigin(); const float bbEnlargement = camera.getNearPlane(); NodeDistancePriorityQueue_F2B distanceQueue(camPos); std::queue<std::pair<Rendering::OcclusionQuery, Node*>> queryQueue; const auto traverseNode = [&](Node& node){ if( node.isClosed() ){ // leaf node context.displayNode(&node, childParam ); }else{ for(auto & child : getChildNodes(&node)) distanceQueue.push(child); } }; const auto pullUpVisibility = [&](Node& node){ for(Node* n=&node; n&&n!=rootNode; n = n->getParent()){ auto& nodeInfo = getNodeInfo(*n); if(nodeInfo.frameNr == frameNr && nodeInfo.visible) break; nodeInfo.visible = true; nodeInfo.frameNr = frameNr; }; }; const auto startQuery = [&](Node& node,const Geometry::Box& worldBoundingBox){ Rendering::OcclusionQuery query; Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); query.begin(); Rendering::drawAbsBox(context.getRenderingContext(), worldBoundingBox ); query.end(); Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); return std::make_pair(std::move(query),&node); }; traverseNode(*rootNode); while(!distanceQueue.empty() || !queryQueue.empty()){ // ---- PART 1: process finished occlusion queries while( !queryQueue.empty() && (distanceQueue.empty() || queryQueue.front().first.isResultAvailable()) ){ if( queryQueue.front().first.getResult()>0 ){ ++stat_numTestsVisible; statistics.pushEvent( Statistics::EVENT_TYPE_END_TEST_VISIBLE, 1); Node& node = *queryQueue.front().second; pullUpVisibility(node); auto& nodeInfo = getNodeInfo( node ); if( !nodeInfo.wasVisible ) traverseNode(node); }else{ ++stat_numTestsInvisible; statistics.pushEvent( Statistics::EVENT_TYPE_END_TEST_INVISIBLE, 1); } queryQueue.pop(); } // ---- PART 2: tree traversal if( !distanceQueue.empty() ){ Node& node = *distanceQueue.top(); distanceQueue.pop(); const Geometry::Box worldBoundingBox = node.getWorldBB(); if (camera.testBoxFrustumIntersection( node.getWorldBB()) == Geometry::Frustum::intersection_t::OUTSIDE) { continue; } Geometry::Box enlargedBox = worldBoundingBox; enlargedBox.resizeAbs( bbEnlargement ); if( enlargedBox.contains(camPos) ){ pullUpVisibility(node); traverseNode(node); continue; } auto& nodeInfo = getNodeInfo( node ); // identify previously visible nodes const bool wasVisible = nodeInfo.frameNr == frameNr-1 && nodeInfo.visible; nodeInfo.wasVisible = wasVisible; // reset node's visibility flag nodeInfo.visible = false; // update node's visited flag nodeInfo.frameNr = frameNr; // test leafs and previously invisible nodes if( node.isClosed() || !wasVisible){ // on testing front // start test ++stat_numTests; statistics.pushEvent(Statistics::EVENT_TYPE_START_TEST, 1); queryQueue.push( std::move(startQuery(node,worldBoundingBox)) ); } // traverse a node unless it was invisible if( wasVisible ) traverseNode( node ); } } statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestVisibleCounter(), stat_numTestsVisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestInvisibleCounter(), stat_numTestsInvisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestCounter(), stat_numTests); } // std::cout << std::endl; return State::STATE_SKIP_RENDERING; }