Example #1
0
//! (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;
}
Example #2
0
//! ---|> [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;
}