/**
 * ---|> [State]
 */
State::stateResult_t StrangeExampleRenderer::doEnableState(FrameContext & context,Node * node, const RenderParam & rp){

	if (rp.getFlag(NO_GEOMETRY) || rp.getFlag(SKIP_RENDERER))
		return State::STATE_SKIPPED;

	/// Collect all nodes in the frustum
	const auto nodes = collectNodesInFrustum<GeometryNode>(node, context.getCamera()->getFrustum());

	if(nodes.size() == 1 && nodes.front() == node) {
		WARN("This renderer must not be attached to GeometryNodes.");
		return State::STATE_SKIPPED;
	}

	/// Render all nodes with a little extra strangeness
	Geometry::Matrix4x4 m;
	float f=0;
	for(const auto & geoNode : nodes) {
		float f2=f+Util::Timer::now();
		m.setIdentity();
		m.scale(1.0+sin( f2)/40.0);
		m.rotate_deg( sin( f2/0.7)/2.0,0,1,0);
		context.getRenderingContext().pushMatrix_modelToCamera();
		context.getRenderingContext().multMatrix_modelToCamera(m);
		context.displayNode(geoNode,rp);
		context.getRenderingContext().popMatrix_modelToCamera();
		f+=137.0;

	}

	return State::STATE_SKIP_RENDERING;
}
void FakeInstanceNode::doDisplay(FrameContext & frameContext, const RenderParam & rp) {
	if(fakePrototype.isNull()) {
		Node::doDisplay(frameContext, rp);
		return;
	}

	auto camera = static_cast<AbstractCameraNode*>(frameContext.getCamera()->clone());
	const Geometry::Matrix4x4f & cameraMatrix = frameContext.getCamera()->getWorldTransformationMatrix();
	camera->setRelTransformation(	fakePrototype->getWorldTransformationMatrix() 
						* getWorldToLocalMatrix() 
						* cameraMatrix);

	frameContext.pushAndSetCamera(camera);
	frameContext.displayNode(fakePrototype.get(), rp);
	frameContext.popCamera();
}
Exemple #3
0
State::stateResult_t OccRenderer::showCulled(FrameContext & context,Node * rootNode, const RenderParam & rp){
	std::deque<Node *> nodes;
	nodes.push_back(rootNode);
	while(!nodes.empty()){
		Node * node=nodes.front();
		nodes.pop_front();
		NodeInfo * nInfo=getNodeInfo(node);

		if(!conditionalFrustumTest(context.getCamera()->getFrustum(), node->getWorldBB(), rp))
			continue;
		else if(node->isClosed() ){
			if(nInfo->getVisibleFrameNumber()!=frameNumber)
				context.displayNode(node,rp);
		}else {
			const auto children = getChildNodes(node);
			nodes.insert(nodes.end(), children.begin(), children.end());
		}
	}
	return State::STATE_SKIP_RENDERING;
}
bool VisibilitySubdivisionRenderer::renderCellSubset(FrameContext & context, cell_ptr cell, uint32_t budgetBegin, uint32_t budgetEnd) {
	try {
		const auto & vv = getVV(cell);

		std::vector<std::pair<float, object_ptr>> displayObjects;
		const uint32_t maxIndex = vv.getIndexCount();
		for(uint_fast32_t index = 0; index < maxIndex; ++index) {
			if(vv.getBenefits(index) == 0) {
				continue;
			}
			const VisibilityVector::costs_t costs = vv.getCosts(index);
			const VisibilityVector::benefits_t benefits = vv.getBenefits(index);
			const float score = static_cast<float>(costs) / static_cast<float>(benefits);
			displayObjects.emplace_back(score, vv.getNode(index));
		}

		const Geometry::Frustum & frustum = context.getCamera()->getFrustum();
		uint32_t renderedTriangles = 0;
		std::sort(displayObjects.begin(), displayObjects.end());
		for(const auto & objectRatioPair : displayObjects) {
			object_ptr o = objectRatioPair.second;
			Geometry::Frustum::intersection_t result = frustum.isBoxInFrustum(o->getWorldBB());
			if (result == Geometry::Frustum::intersection_t::INSIDE || result == Geometry::Frustum::intersection_t::INTERSECT) {
				if(renderedTriangles >= budgetBegin) {
					context.displayNode(o, 0);
				}
				renderedTriangles += o->getTriangleCount();
			}

			if (budgetEnd != 0 && renderedTriangles >= budgetEnd) {
				break;
			}
		}
	} catch(...) {
		// Invalid information. => Fall back to standard rendering.
		return false;
	}
	return true;
}
Exemple #5
0
/**
 * (internal)
 */
void OccRenderer::processNode(FrameContext & context,Node * node,NodeInfo * nodeInfo,NodeDistancePriorityQueue_F2B & distanceQueue,const RenderParam & rp){
	if (!node->isActive())
		return;

	nodeInfo->setProcessedFrameNumber(frameNumber);

	if(node->isClosed()){
		context.displayNode(node, rp );
	}else{
		const auto children = getChildNodes(node);
		for(auto & child : children){
			NodeInfo * i=getNodeInfo(child);
			if( rp.getFlag(FRUSTUM_CULLING) && i->getActualFrustumStatus()==Geometry::Frustum::OUTSIDE) //context.getCamera()->testBoxFrustumIntersection( (*it)->getWorldBB()) == Frustum::OUTSIDE )
				continue;
			if( i->getActualSubtreeComplexity() == 0){
				continue;
			}

			distanceQueue.push(child);
		}
	}
}
void VisibilitySubdivisionRenderer::debugDisplay(uint32_t & renderedTriangles, object_ptr object, FrameContext & context, const RenderParam & rp) {
	const Util::Color4f specular(0.1f, 0.1f, 0.1f, 1.0f);


	const float ratio = static_cast<float> (renderedTriangles) / static_cast<float> (maxRuntime);

	// Gradient from light blue to red.
	Util::Color4f color(ratio, 0.5f - 0.5f * ratio, 1.0f - ratio, 1.0f);

	Rendering::MaterialParameters material;
	material.setAmbient(color);
	material.setDiffuse(color);
	material.setSpecular(specular);
	material.setShininess(64.0f);

	context.getRenderingContext().pushAndSetMaterial(material);

	context.displayNode(object, rp);

	context.getRenderingContext().popMaterial();

	renderedTriangles += object->getTriangleCount();
}
//! ---|> [State]
State::stateResult_t EnvironmentState::doEnableState(FrameContext & context, Node * /*node*/, const RenderParam & rp) {

	if (rp.getFlag(NO_GEOMETRY))
		return State::STATE_SKIPPED;

	if (!environment.isNull()) {

		// extract rotation of the camera
		Geometry::SRT camRotationSrt = context.getRenderingContext().getMatrix_worldToCamera()._toSRT();
		camRotationSrt.setTranslation(Geometry::Vec3(0,0,0));
		camRotationSrt.setScale(1.0);

		// render the environment node as seen standing at the origin (0,0,0) but looking in the camera's direction.
		context.getRenderingContext().pushMatrix_modelToCamera();
		context.getRenderingContext().setMatrix_modelToCamera(Geometry::Matrix4x4(camRotationSrt));
		context.getRenderingContext().multMatrix_modelToCamera(Geometry::Matrix4x4f(Geometry::SRT(Geometry::Vec3f(0,0,0), context.getWorldFrontVector(), context.getWorldUpVector())));
		context.displayNode(environment.get(), rp);
		context.getRenderingContext().popMatrix_modelToCamera();

	}

	return State::STATE_OK;
}
void ColorVisibilityEvaluator::measure(FrameContext & context, Node & node, const Geometry::Rect & rect) {
	Rendering::RenderingContext & rCtxt = context.getRenderingContext();

	// ### Set up textures for the framebuffer object. ###
	const uint32_t width = static_cast<uint32_t> (rect.getWidth());
	const uint32_t height = static_cast<uint32_t> (rect.getHeight());
	if (colorTexture == nullptr || width != static_cast<uint32_t> (colorTexture->getWidth()) || height != static_cast<uint32_t> (colorTexture->getHeight())) {
		// (Re-)Create textures with correct dimension.
		rCtxt.pushAndSetFBO(fbo.get());
		fbo->detachColorTexture(context.getRenderingContext());
		fbo->detachDepthTexture(context.getRenderingContext());
		rCtxt.popFBO();

		colorTexture = Rendering::TextureUtils::createStdTexture(width, height, true);
		depthTexture = Rendering::TextureUtils::createDepthTexture(width, height);

		// Bind textures to FBO.
		rCtxt.pushAndSetFBO(fbo.get());
		fbo->attachColorTexture(context.getRenderingContext(),colorTexture.get());
		fbo->attachDepthTexture(context.getRenderingContext(),depthTexture.get());
		rCtxt.popFBO();
	}

	// ### Render the scene into a framebuffer object. ###
	rCtxt.pushAndSetFBO(fbo.get());

	rCtxt.pushAndSetShader(getShader());
	rCtxt.clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 0.0f));


	// Color counter for assignment of unique colors to triangles.
	// Background is black (color 0).
	int32_t currentColor = 1;


	// Collect potentially visible geometry nodes.
	const auto geoNodes = collectNodesInFrustum<GeometryNode>(&node, context.getCamera()->getFrustum());
	for(const auto & geoNode : geoNodes) {
		getShader()->setUniform(rCtxt, Rendering::Uniform("colorOffset", currentColor));
		context.displayNode(geoNode, USE_WORLD_MATRIX | NO_STATES);
		currentColor += geoNode->getTriangleCount();
	}

	rCtxt.popShader();
	rCtxt.popFBO();


	// ### Read texture and analyze colors. ###

	// Map from colors to number of occurrences.
	std::map<uint32_t, uint32_t> colorUsage;


	// Current color is compared with the previous color. If they match, the local count variable is used as cache before insertion into map is performed.
	uint32_t lastColor = 0;
	uint32_t lastCount = 0;

	colorTexture->downloadGLTexture(rCtxt);
	const uint32_t * texData = reinterpret_cast<const uint32_t *> (colorTexture->openLocalData(rCtxt));
	const uint32_t numPixels = width * height;
	for (uint_fast32_t p = 0; p < numPixels; ++p) {
		const uint32_t & color = texData[p];

		if (color == 0) {
			// Ignore background.
			continue;
		}

		if (color == lastColor) {
			++lastCount;
			continue;
		} else {
			// Insert previous color.
			if (lastColor != 0) {
				increaseColorUsage(lastColor, lastCount, colorUsage);
			}

			lastColor = color;
			lastCount = 1;
		}
	}
	if (lastColor != 0) {
		increaseColorUsage(lastColor, lastCount, colorUsage);
	}

	if (mode == SINGLE_VALUE) {
		numTrianglesVisible += colorUsage.size();
	} else {
		values->push_back(Util::GenericAttribute::createNumber(colorUsage.size()));
	}
}
//! ---|> Evaluator
void OccOverheadEvaluator::measure(FrameContext & context,Node & node,const Geometry::Rect & /*r*/) {

	//float currentRenderingTime=0;
	//int numTests=0;
	//int numVBO=0;
	int numPoly=0;
	Util::Color4f clearColor(0.5f, 0.5f, 0.9f, 1.0f);
	{ // 1. normal render pass
//        rc.clearScreen(0.5,0.5,0.9);
//        context.getRenderingContext().finish();
//        node.display(rc,renderingFlags);
		context.getRenderingContext().clearScreen(clearColor);
		node.display(context,renderingFlags|USE_WORLD_MATRIX);

		context.getRenderingContext().clearScreen(clearColor);
		context.getRenderingContext().finish();
		context.getStatistics().beginFrame();
		node.display(context,renderingFlags|USE_WORLD_MATRIX);
		context.getRenderingContext().finish();
		context.getStatistics().endFrame();
		numPoly=static_cast<int>(context.getStatistics().getValueAsDouble(context.getStatistics().getTrianglesCounter()));
	}

	// 2. test all group nodes if their bb is visible and collect them
	std::deque<GroupNode *> visibleGroupNodes;

	{
		// collect geometry nodes in frustum
		const auto groupNodesInVFList = collectNodesInFrustum<GroupNode>(&node,context.getCamera()->getFrustum());

		// setup occlusion queries
		int numQueries=groupNodesInVFList.size();
		if (numQueries==0) {
			values->push_back(nullptr); // TODO!!!
			return; // TODO??
		}

		auto queries = new Rendering::OcclusionQuery[numQueries];

		//  test bounding boxes
		context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::LEQUAL));
		int i=0;
		// start queries
		Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext());
		for(const auto & groupNode : groupNodesInVFList) {
			queries[i].begin();
//            queries[i].fastBoxTest((*it)->getWorldBB());
			Rendering::drawAbsBox(context.getRenderingContext(), groupNode->getWorldBB() );
			queries[i].end();
			++i;
		}
		Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext());
		i=0;
		// get results
		for(const auto & groupNode : groupNodesInVFList) {
			Geometry::Box extendedBox=groupNode->getWorldBB();
			extendedBox.resizeAbs(context.getCamera()->getNearPlane());

			if (queries[i].getResult() > 0 || extendedBox.contains(context.getCamera()->getWorldOrigin()) ) {
				visibleGroupNodes.push_back(groupNode);
			}
			++i;
		}
		context.getRenderingContext().popDepthBuffer();

		delete [] queries;
	}
	std::deque<GeometryNode *> geoNodesInVisibleGroupNodes;

	// 3. collect all direct geometry-node children
	{
		struct GeoChildrenCollector:public NodeVisitor {
			GeoChildrenCollector(std::deque<GeometryNode*> & _geoNodes,FrameContext & _context):
				geoNodes(_geoNodes),firstNode(true),frameContext(_context) {}
			virtual ~GeoChildrenCollector() {}

			std::deque<GeometryNode*> & geoNodes;
			bool firstNode;
			FrameContext & frameContext;

			// ---|> NodeVisitor
			NodeVisitor::status enter(Node * _node) override {
				// if this is the first call, continue...
				if(firstNode) {
					firstNode=false;
					return CONTINUE_TRAVERSAL;
				}
				// else collect the geometry children and break the traversal.
				GeometryNode * gn=dynamic_cast<GeometryNode *>(_node);
				if(gn){
					if(frameContext.getCamera()->testBoxFrustumIntersection( _node->getWorldBB()) != Geometry::Frustum::intersection_t::OUTSIDE)
						geoNodes.push_back(gn);
				}
				return BREAK_TRAVERSAL;
			}
		};
		for (auto & visibleGroupNode : visibleGroupNodes) {
			GeoChildrenCollector vis(geoNodesInVisibleGroupNodes,context);
			(visibleGroupNode)->traverse(vis);
		}

	}

	// 4. clear screen and measure time drawing children
	float perfectRenderingTime=0;
	{
		context.getRenderingContext().clearScreen(clearColor);

		context.getRenderingContext().finish();
		Util::Timer timer;
		timer.reset();

		for (auto & geoNodesInVisibleGroupNode : geoNodesInVisibleGroupNodes) {
			context.displayNode((geoNodesInVisibleGroupNode),renderingFlags|USE_WORLD_MATRIX);
		}

		context.getRenderingContext().finish();
		timer.stop();
		perfectRenderingTime=timer.getMilliseconds();
	}

//    // Test-costs:
//    float result=numTests!=0?(currentRenderingTime-perfectRenderingTime)/numTests:0;

	// currentRenderingTime-perfRendering:
//    float result=currentRenderingTime-perfectRenderingTime;

	float result=numPoly>0?perfectRenderingTime*1000/numPoly:0;

//    std::cout <<currentRenderingTime<<"-"<<perfectRenderingTime<<"="<<result<<"\t"<<numTests<<"\n";
//    std::cout <<numVBO<<":"<<geoNodesInVisibleGroupNodes.size()<<"\n";

//   float result=perfectRenderingTime;
//    values.push_back(currentRenderingTime);
//    values.push_back(perfectRenderingTime);
	values->push_back(new Util::_NumberAttribute<float>(result));

//    if(mode==SINGLE_VALUE){
//        if(result>renderingTime || renderingTime==0)
//            renderingTime=result;
//    }else{
//        values.push_back(result);
//    }

//    renderingTime=0;
}
State::stateResult_t VisibilitySubdivisionRenderer::doEnableState(
																	FrameContext & context,
																	Node *,
																	const RenderParam & rp) {
	if (rp.getFlag(SKIP_RENDERER)) {
		return State::STATE_SKIPPED;
	}

	if (viSu == nullptr) {
		// Invalid information. => Fall back to standard rendering.
		return State::STATE_SKIPPED;
	}

	// [AccumRendering]
	if(accumRenderingEnabled){
		// camera moved? -> start new frame
		const Geometry::Matrix4x4 & newCamMat=context.getCamera()->getWorldTransformationMatrix();
		if(newCamMat!=lastCamMatrix){
			lastCamMatrix = newCamMat;
			startRuntime=0;
		}
	}else{
		startRuntime=0;
	}
	// ----

	uint32_t renderedTriangles = 0;
	if (hold) {
		for (const auto & object : holdObjects) {
			if (debugOutput) {
				debugDisplay(renderedTriangles, object, context, rp);
			} else {
				context.displayNode(object, rp);
			}
		}
		return State::STATE_SKIP_RENDERING;
	}

	Geometry::Vec3 pos = context.getCamera()->getWorldOrigin();
	bool refreshCache = false;
	// Check if cached cell can be used.
	if (currentCell == nullptr || !currentCell->getBB().contains(pos)) {
		currentCell = viSu->getNodeAtPosition(pos);
		refreshCache = true;
	}
	if (currentCell == nullptr || !currentCell->isLeaf()) {
		// Invalid information. => Fall back to standard rendering.
		return State::STATE_SKIPPED;
	}

	if (refreshCache) {
		try {
			const auto & vv = getVV(currentCell);

			const uint32_t maxIndex = vv.getIndexCount();
			objects.clear();
			objects.reserve(maxIndex);
			for(uint_fast32_t index = 0; index < maxIndex; ++index) {
				if(vv.getBenefits(index) == 0) {
					continue;
				}
				const VisibilityVector::costs_t costs = vv.getCosts(index);
				const VisibilityVector::benefits_t benefits = vv.getBenefits(index);
				const float score = static_cast<float>(costs) / static_cast<float>(benefits);
				objects.emplace_back(score, vv.getNode(index));
			}
		} catch(...) {
			// Invalid information. => Fall back to standard rendering.
			return State::STATE_SKIPPED;
		}

		if (displayTexturedDepthMeshes) {
#ifdef MINSG_EXT_OUTOFCORE
			for (const auto & depthMesh : depthMeshes) {
				OutOfCore::getCacheManager().setUserPriority(depthMesh.get(), 0);
			}
#endif /* MINSG_EXT_OUTOFCORE */
			depthMeshes.clear();
			depthMeshes.reserve(6);
			textures.clear();
			textures.reserve(6);

			const std::string dmDirectionStrings[6] = { "-dir_x1_y0_z0", "-dir_x-1_y0_z0",
														"-dir_x0_y1_z0", "-dir_x0_y-1_z0",
														"-dir_x0_y0_z1", "-dir_x0_y0_z-1" };
			for (auto & dmDirectionString : dmDirectionStrings) {
				Util::GenericAttribute * attrib = currentCell->getAttribute("DepthMesh" + dmDirectionString);
				if (attrib == nullptr) {
					continue;
				}
				Util::FileName dmMeshPath(attrib->toString());
				Util::Reference<Rendering::Mesh> dmMesh;
#ifdef MINSG_EXT_OUTOFCORE
				Util::GenericAttribute * bbAttrib = currentCell->getAttribute("DepthMesh" + dmDirectionString + "-bounds");
				if (bbAttrib == nullptr) {
					WARN("Found depth mesh with no bounding box.");
					continue;
				}

				std::vector<float> bbValues = Util::StringUtils::toFloats(bbAttrib->toString());
				FAIL_IF(bbValues.size() != 6);
				const Geometry::Box meshBB(Geometry::Vec3(bbValues[0], bbValues[1], bbValues[2]), bbValues[3], bbValues[4], bbValues[5]);

				dmMesh = OutOfCore::addMesh(dmMeshPath, meshBB);
#else /* MINSG_EXT_OUTOFCORE */
				dmMesh = Rendering::Serialization::loadMesh(dmMeshPath);
#endif /* MINSG_EXT_OUTOFCORE */
				depthMeshes.emplace_back(dmMesh);
				// Count the depth mesh here already.
				renderedTriangles += dmMesh->getPrimitiveCount();

				Util::GenericAttribute * texAttrib = currentCell->getAttribute("Texture" + dmDirectionString);
				if (texAttrib == nullptr) {
					continue;
				}
				Util::FileName texturePath(texAttrib->toString());
				Util::Reference<Rendering::Texture> texture = Rendering::Serialization::loadTexture(texturePath);
				if (texture.isNull()) {
					WARN("Loading texture for depth mesh failed.");
					continue;
				}
				textures.emplace_back(texture);
			}
		}
	}

	const Geometry::Frustum & frustum = context.getCamera()->getFrustum();
	holdObjects.clear();
	holdObjects.reserve(objects.size());

	std::sort(objects.begin(), objects.end());
	for(const auto & ratioObjectPair : objects) {
		object_ptr o = ratioObjectPair.second;
#ifdef MINSG_EXT_OUTOFCORE
		OutOfCore::getCacheManager().setUserPriority(o->getMesh(), 5);
#endif /* MINSG_EXT_OUTOFCORE */
		
		if (conditionalFrustumTest(frustum, o->getWorldBB(), rp)) {
			// [AccumRendering]
			// skip geometry rendered in the last frame
			if(renderedTriangles<startRuntime){
				renderedTriangles += o->getTriangleCount();
				continue;
			}
			// ----

			if (debugOutput) {
				debugDisplay(renderedTriangles, o, context, rp);
			} else {
				context.displayNode(o, rp);
				renderedTriangles += o->getTriangleCount();
			}
			holdObjects.push_back(o);
		}
		if (maxRuntime != 0 && renderedTriangles >= startRuntime+maxRuntime) {
			break;
		}
	}

	// Draw the textured depth meshes at the end.
	if (displayTexturedDepthMeshes) {
		context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(polygonOffsetFactor, polygonOffsetUnits));
		auto texIt = textures.cbegin();
		for (const auto & depthMesh : depthMeshes) {
			context.getRenderingContext().pushAndSetShader(getTDMShader());
			context.getRenderingContext().pushAndSetTexture(0,texIt->get());
			
			if (conditionalFrustumTest(frustum, depthMesh->getBoundingBox(), rp)) {
				context.displayMesh(depthMesh.get());
			}
			context.getRenderingContext().popTexture(0);
			context.getRenderingContext().popShader();
#ifdef MINSG_EXT_OUTOFCORE
			OutOfCore::getCacheManager().setUserPriority(depthMesh.get(), 2);
#endif /* MINSG_EXT_OUTOFCORE */
			++texIt;
		}
		context.getRenderingContext().popPolygonOffset();
	}
	// [AccumRendering]
	startRuntime=renderedTriangles;
	// ----
	return State::STATE_SKIP_RENDERING;
}
Exemple #11
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;
}
//! ---|> Evaluator
void VisibilityEvaluator::measure(FrameContext & context,Node & node,const Geometry::Rect & /*r*/) {
	// first pass - fill depth buffer.
	context.getRenderingContext().clearScreen(Util::Color4f(0.9f, 0.9f, 0.9f, 1.0f));
	node.display(context,USE_WORLD_MATRIX|FRUSTUM_CULLING);

	if (getMaxValue()->toInt() == 0.0) {
		setMaxValue_i(collectNodes<GeometryNode>(&node).size());
	}

	if (mode==SINGLE_VALUE) {

		// collect geometry nodes in frustum
		auto objectsInVFList = collectNodesInFrustum<GeometryNode>(&node, context.getCamera()->getFrustum());
		for(const auto & geoNode : objectsInVFList) {
			objectsInVF[reinterpret_cast<uintptr_t>(geoNode)] = geoNode;
		}


		// setup occlusion queries
		int numQueries=objectsInVFList.size();
		if (numQueries==0) return;


		auto queries = new Rendering::OcclusionQuery[numQueries];

		{ // optional second pass: test bounding boxes
			context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::LEQUAL));
			context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(0.0f, -1.0f));
			int i=0;
			// start queries
			Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext());
			for(const auto & geoNode : objectsInVFList) {
				queries[i].begin();
				Rendering::drawAbsBox(context.getRenderingContext(), geoNode->getWorldBB() );
				queries[i].end();
				++i;
			}
			Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext());
			i=0;
			// get results
			for(auto & geoNode : objectsInVFList) {
				if (queries[i].getResult() == 0 && !geoNode->getWorldBB().contains(context.getCamera()->getWorldOrigin()) ) {
					geoNode=nullptr;
				}
				++i;
			}
			context.getRenderingContext().popPolygonOffset();
			context.getRenderingContext().popDepthBuffer();
		}


		{ // third pass: test if nodes are exactly visible
			context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::EQUAL));
			int i=0;
			// start queries
			for(const auto & geoNode : objectsInVFList) {
				if (!geoNode)
					continue;
				queries[i].begin();
				// render object i
				context.displayNode(geoNode, USE_WORLD_MATRIX);
				queries[i].end();
				++i;
			}
			i=0;
			// get results
			for(const auto & geoNode : objectsInVFList) {
				if (!geoNode)
					continue;
				if (queries[i].getResult() > 0) {
					visibleObjects[reinterpret_cast<uintptr_t>(geoNode)] = geoNode;
				}
				++i;
			}
			context.getRenderingContext().popDepthBuffer();
		}

		delete [] queries;
	} else {//mode == DIRECTION_VALUES
		const auto nodes = collectVisibleNodes(&node, context);
		if(doesCountPolygons()){
			uint32_t numPolys=0;
			for(const auto & n : nodes) {
				numPolys += n->getTriangleCount();
			}
			values->push_back(new Util::_NumberAttribute<float>(numPolys));
		}else{
			values->push_back(new Util::_NumberAttribute<float>(nodes.size()));
		}

	}
}
void OverdrawFactorEvaluator::measure(FrameContext & frameContext, Node & node, const Geometry::Rect & rect) {
	Rendering::RenderingContext & renderingContext = frameContext.getRenderingContext();

	// Set up FBO and texture
	Util::Reference<Rendering::FBO> fbo = new Rendering::FBO;
	renderingContext.pushAndSetFBO(fbo.get());
	Util::Reference<Rendering::Texture> depthStencilTexture = Rendering::TextureUtils::createDepthStencilTexture(rect.getWidth(), rect.getHeight());
	fbo->attachDepthStencilTexture(renderingContext, depthStencilTexture.get());

	// Disable color and depth writes
	renderingContext.pushAndSetColorBuffer(Rendering::ColorBufferParameters(false, false, false, false));
	renderingContext.pushAndSetDepthBuffer(Rendering::DepthBufferParameters(false, false, Rendering::Comparison::LESS));

	// Increase the stencil value for every rendered pixel
	Rendering::StencilParameters stencilParams;
	stencilParams.enable();
	stencilParams.setFunction(Rendering::Comparison::ALWAYS);
	stencilParams.setReferenceValue(0);
	stencilParams.setBitMask(0);
	stencilParams.setFailAction(Rendering::StencilParameters::INCR);
	stencilParams.setDepthTestFailAction(Rendering::StencilParameters::INCR);
	stencilParams.setDepthTestPassAction(Rendering::StencilParameters::INCR);
	renderingContext.pushAndSetStencil(stencilParams);

	// Render the node
	renderingContext.clearStencil(0);
	frameContext.displayNode(&node, 0);

	// Reset GL state
	renderingContext.popStencil();
	renderingContext.popDepthBuffer();
	renderingContext.popColorBuffer();

	// Fetch the texture.
	depthStencilTexture->downloadGLTexture(renderingContext);
	renderingContext.popFBO();

	Util::Reference<Util::PixelAccessor> stencilAccessor = Rendering::TextureUtils::createStencilPixelAccessor(renderingContext, *depthStencilTexture.get());

	std::vector<uint8_t> stencilValues;
	stencilValues.reserve(rect.getWidth() * rect.getHeight());
	for(uint_fast32_t y = 0; y < rect.getHeight(); ++y) {
		for(uint_fast32_t x = 0; x < rect.getWidth(); ++x) {
			const uint8_t stencilValue = stencilAccessor->readSingleValueByte(x, y);
			stencilValues.push_back(stencilValue);
		}
	}

// 	// Create and write debug image
// 	{
// 		Util::Reference<Rendering::Texture> colorTexture = Rendering::TextureUtils::createStdTexture(rect.getWidth(), rect.getHeight(), true);
// 		Util::Reference<Util::PixelAccessor> colorAccessor = Rendering::TextureUtils::createColorPixelAccessor(renderingContext, colorTexture.get());
// 		const auto stencilMinMax = std::minmax_element(stencilValues.cbegin(), stencilValues.cend());
// 		const auto stencilMin = *stencilMinMax.first;
// 		const auto stencilMax = *stencilMinMax.second;
// 		const double stencilRange = stencilMax - stencilMin;
// 		const double factor = 1.0 / stencilRange;
// 		// Color scheme RdYlBu with 5 of 8 colors from www.colorbrewer2.org
// 		const std::array<Util::Color4f, 5> gradient =	{
// 															Util::Color4ub(44, 123, 182, 255),
// 															Util::Color4ub(171, 217, 233, 255),
// 															Util::Color4ub(255, 255, 191, 255),
// 															Util::Color4ub(253, 174, 97, 255),
// 															Util::Color4ub(215, 25, 28, 255)
// 														};
// 		for(uint_fast32_t y = 0; y < rect.getHeight(); ++y) {
// 			for(uint_fast32_t x = 0; x < rect.getWidth(); ++x) {
// 				const uint8_t stencilValue = stencilValues[y * rect.getWidth() + x];
// 				if(stencilValue == 0) {
// 					colorAccessor->writeColor(x, y, Util::Color4f(1.0, 1.0, 1.0, 0.0));
// 				} else {
// 					const double normalizedValue = static_cast<double>(stencilValue - stencilMin) * factor;
// 					const double gradientPos = normalizedValue * (gradient.size() - 1);
// 					const size_t gradientIndex = std::floor(gradientPos);
// 					colorAccessor->writeColor(x, y, Util::Color4f(gradient[gradientIndex], gradient[gradientIndex + 1], gradientPos - gradientIndex));
// 				}
// 			}
// 		}
// 		Rendering::Serialization::saveTexture(renderingContext, colorTexture.get(), Util::FileName("stencil.png"));
// 	}

	if(resultRemoveZeroValues) {
		stencilValues.erase(std::remove(stencilValues.begin(), stencilValues.end(), 0),
							stencilValues.end());
	}
	uint8_t result = 0;
	if(!stencilValues.empty()) {
		if(resultQuantile >= 1.0) {
			result = *std::max_element(stencilValues.cbegin(), stencilValues.cend());
		} else if(resultQuantile <= 0.0) {
			result = *std::min_element(stencilValues.cbegin(), stencilValues.cend());
		} else {
			const std::size_t quantilePos = resultQuantile * stencilValues.size();
			std::nth_element(stencilValues.begin(),
							 std::next(stencilValues.begin(), static_cast<std::ptrdiff_t>(quantilePos)),
							 stencilValues.end());
			result = stencilValues[quantilePos];
		}
	}

	values->push_back(Util::GenericAttribute::createNumber(result));
	setMaxValue_i(result);
}
/*! @note very important: color cubes of the children must already exist, otherwise there will be a segmentation fault  */
void ColorCubeGenerator::processColorCube(FrameContext& context, Node * node, deque<Node*>& children) {

	const Geometry::Box box = node->getWorldBB();
	ColorCube cc;

	// 1. case: current node's color cube should be processed by drawing Geometry < see processColorCubes(...) >
	if (children.empty()) {
		context.getRenderingContext().pushAndSetLighting(Rendering::LightingParameters(true));
		context.getRenderingContext().applyChanges();

		// render the six sides
		context.getRenderingContext().pushAndSetFBO(fbo.get());
		for (uint8_t _side = 0; _side < 6; ++_side) { // determine the color for each of the 6 faces
			const Geometry::side_t side = static_cast<Geometry::side_t> (_side);
			if (!prepareCamera(context, box, side))
				continue;

			context.getRenderingContext().clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 0.0f));
			context.displayNode(node, USE_WORLD_MATRIX);
		}
		context.getRenderingContext().popFBO();
		colorTexture->downloadGLTexture(context.getRenderingContext());

		//////////// debug
// 		static uint32_t counter=0;
// 		stringstream ss;
// 		ss << "screens/colorcube_" << counter++ << ".png";
// 		Util::FileName filename(ss.str());
// 		Rendering::Serialization::saveTexture(context.getRenderingContext(), colorTexture.get(), filename);
		//////////// end debug



		// determine the color for each of the 6 faces
		for (uint8_t _side = 0; _side < 6; ++_side) {
			const Geometry::side_t side = static_cast<Geometry::side_t> (_side);
			if (prepareCamera(context, box, side)){
				const Color4ub c = calculateColor(colorTexture.get(), camera->getViewport()); //calculate color from texture
				cc.colors[static_cast<uint8_t> (side)] = c;
			} else {
				cc.colors[static_cast<uint8_t> (side)] = Color4ub(0, 0, 0, 0);
			}
		}

		context.getRenderingContext().popLighting();
	}
	else{
		context.getRenderingContext().pushAndSetFBO(fbo.get()); // enable the fbo

		// 2. case:  the node is not a closed GroupNode  (inner node)
		for (uint8_t _side = 0; _side < 6; ++_side) { // for each of the six faces
			Geometry::side_t side = static_cast<Geometry::side_t> (_side);
			if (!prepareCamera(context, box, side))
				continue;

			context.getRenderingContext().clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 0.0f));
			context.getRenderingContext().pushAndSetMatrix_modelToCamera( context.getRenderingContext().getMatrix_worldToCamera() );


			// draw faces using blending
			context.getRenderingContext().pushAndSetBlending(Rendering::BlendingParameters(Rendering::BlendingParameters::ONE, Rendering::BlendingParameters::ONE_MINUS_SRC_ALPHA));
			context.getRenderingContext().pushAndSetCullFace(Rendering::CullFaceParameters(Rendering::CullFaceParameters::CULL_BACK));
			context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, false, Rendering::Comparison::LESS));
			context.getRenderingContext().pushAndSetLighting(Rendering::LightingParameters(false));
			context.getRenderingContext().applyChanges();

			distanceQueue_t nodes(side); // distance queue used for sorting the children with color cubes

			// sort (back-to-front order) the children according to distance of current side to the camera
			for(const auto & child : children) {
				nodes.push(child);
			}

			// draw the faces in back-to-front order
			while (!nodes.empty()) {
				Node* child = nodes.top();
				nodes.pop();
				const ColorCube & childsColorCube = ColorCube::getColorCube(child);
				//cerr << "before drawing colored box " << endl;
				childsColorCube.drawColoredBox(context, child->getWorldBB());
				//cerr << "after drawing colored box" << endl;
			}

			context.getRenderingContext().popLighting();
			context.getRenderingContext().popDepthBuffer();
			context.getRenderingContext().popCullFace();
			context.getRenderingContext().popBlending();
			context.getRenderingContext().popMatrix_modelToCamera();
		}
		context.getRenderingContext().popFBO();
		colorTexture->downloadGLTexture(context.getRenderingContext());
		// determine the color for each of the 6 faces
		for (uint8_t _side = 0; _side < 6; ++_side) {
			Geometry::side_t side = static_cast<Geometry::side_t> (_side);
			if (!prepareCamera(context, box, side))
				continue;
			//calculate color from texture
			cc.colors[static_cast<uint8_t> (side)] = calculateColor(colorTexture.get(), camera->getViewport());
		}
	}

	ColorCube::attachColorCube(node, cc);
}
void MultiAlgoGroupNode::doDisplay(FrameContext & context, const RenderParam & rp) {		
	context.displayNode(node.get(), rp);
}