示例#1
0
void Exporter::exportToFile(const QString& filename,
							const vector<PtrPrimitive>& primitive_tab,
							VRenderParams& vparams)
{
	QFile file(filename);

	if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
		QMessageBox::warning(NULL, QGLViewer::tr("Exporter error", "Message box window title"), QGLViewer::tr("Unable to open file %1.").arg(filename));
		return;
	}

	QTextStream out(&file);

	writeHeader(out) ;

		unsigned int N = primitive_tab.size()/200 + 1 ;

	for(unsigned int i=0;i<primitive_tab.size();++i)
	{
		Point *p = dynamic_cast<Point *>(primitive_tab[i]) ;
		Segment *s = dynamic_cast<Segment *>(primitive_tab[i]) ;
		Polygone *P = dynamic_cast<Polygone *>(primitive_tab[i]) ;

		if(p != NULL) spewPoint(p,out) ;
		if(s != NULL) spewSegment(s,out) ;
		if(P != NULL) spewPolygone(P,out) ;

		if(i%N == 0)
			vparams.progress(i/(float)primitive_tab.size(),QGLViewer::tr("Exporting to file %1").arg(filename)) ;
	}

	writeFooter(out) ;

	file.close();
}
示例#2
0
void BSPSortMethod::sortPrimitives(std::vector<PtrPrimitive>& primitive_tab,VRenderParams& vparams)
{
	// 1 - build BSP using polygons only

	BSPTree tree;
	Polygone *P;

	int N = primitive_tab.size()/200 +1;
	int nbinserted = 0;

	vector<PtrPrimitive> segments_and_points;	// Store segments and points for pass 2, because polygons are deleted
																// by the insertion and can not be dynamic_casted anymore.
	for(unsigned int i=0;i<primitive_tab.size();++i,++nbinserted)
	{
		if((P = dynamic_cast<Polygone *>(primitive_tab[i])) != NULL)
			tree.insert(P);
		else
			segments_and_points.push_back(primitive_tab[i]);

		if(nbinserted%N==0)
			vparams.progress(nbinserted/(float)primitive_tab.size(), QGLViewer::tr("BSP Construction"));
	}

	// 2 - insert points and segments into the BSP

	Segment *S;
	Point *p;

	for(unsigned int j=0;j<segments_and_points.size();++j,++nbinserted)
	{
		if((S = dynamic_cast<Segment *>(segments_and_points[j])) != NULL)
			tree.insert(S);
		else if((p = dynamic_cast<Point *>(segments_and_points[j])) != NULL)
			tree.insert(p);

		if(nbinserted%N==0)
			vparams.progress(nbinserted/(float)primitive_tab.size(), QGLViewer::tr("BSP Construction"));
	}

	// 3 - refill the array with the content of the BSP

	primitive_tab.resize(0);
	tree.recursFillPrimitiveArray(primitive_tab);
}
void TopologicalSortUtils::recursTopologicalSort(	vector< vector<int> >& precedence_graph,
																	vector<PtrPrimitive>& primitive_tab,
																	vector<bool>& already_rendered,
																	vector<bool>& already_visited,
																	vector<PtrPrimitive>& new_pr_tab,
																	int indx,
																	int& nb_cycles,
																	VRenderParams& vparams,
																	int info_cnt,int& nbrendered)
{
	// One must first render the primitives indicated by the precedence graph,
	// then render the current primitive. Skews are detected, but and treated.

	already_visited[indx] = true ;

	for(unsigned int j=0;j<precedence_graph[indx].size();++j)
	{
		// Both tests are important. If we ommit the second one, the recursion is
		// always performed down to the next cycle, although this is useless if
		// the current primitive was rendered already.

		if(!already_visited[precedence_graph[indx][j]])
		{
			if(!already_rendered[precedence_graph[indx][j]])
				recursTopologicalSort(	precedence_graph,primitive_tab,already_rendered,already_visited,
												new_pr_tab,precedence_graph[indx][j],nb_cycles,vparams,info_cnt,nbrendered) ;
		}
		else //  A cycle is detected, but in this version, it is not broken.
			++nb_cycles ;
	}

	if(!already_rendered[indx])
	{
		new_pr_tab.push_back(primitive_tab[indx]) ;

		if((++nbrendered)%info_cnt==0)
			vparams.progress(nbrendered/(float)primitive_tab.size(), QGLViewer::tr("Topological sort")) ;
	}

	already_rendered[indx] = true ;
	already_visited[indx] = false ;
}
示例#4
0
void ParserGL::parseFeedbackBuffer(	GLfloat *buffer,int size,
												std::vector<PtrPrimitive>& primitive_tab,
												VRenderParams& vparams)
{
	int token;
	int nvertices = 0 ;
	nb_lines = 0 ;
	nb_polys = 0 ;
	nb_points = 0 ;
	nb_degenerated_lines = 0 ;
	nb_degenerated_polys = 0 ;
	nb_degenerated_points = 0 ;

	// pre-treatment of coordinates so as to get something more consistent

	_xmin = FLT_MAX ;
	_ymin = FLT_MAX ;
	_zmin = FLT_MAX ;
	_xmax = -FLT_MAX ;
	_ymax = -FLT_MAX ;
	_zmax = -FLT_MAX ;

	ParserUtils::ComputeBufferBB(size, buffer, _xmin,_xmax,_ymin,_ymax,_zmin,_zmax) ;

#ifdef DEBUGEPSRENDER
	printf("Buffer bounding box: %f %f %f %f %f %f\n",xmin,xmax,ymin,ymax,zmin,zmax) ;
#endif
	float Zdepth = max(_ymax-_ymin,_xmax-_xmin) ;
	ParserUtils::NormalizeBufferCoordinates(size,buffer,Zdepth,_zmin,_zmax) ;

	// now, read buffer
	GLfloat *end = buffer + size;

	GLfloat *loc = buffer ;
	int next_step = 0 ;
	int N = size/200 + 1 ;

	while (loc < end)
	{
		token = int(0.5f + *loc) ;
		loc++;

		if((end-loc)/N >= next_step)
			vparams.progress((end-loc)/(float)size, QGLViewer::tr("Parsing feedback buffer.")), ++next_step ;

		switch (token)
		{
			case GL_LINE_TOKEN:
			case GL_LINE_RESET_TOKEN:
				{
					Segment *S = new Segment(Feedback3DColor(loc),Feedback3DColor(loc+Feedback3DColor::sizeInBuffer())) ;

					primitive_tab.push_back(ParserUtils::checkSegment(S)) ;

					if(S == NULL)
						nb_degenerated_lines++ ;

					nb_lines++ ;
					loc += 2*Feedback3DColor::sizeInBuffer();
				}
				break;

			case GL_POLYGON_TOKEN:
				{
					nvertices = int(0.5f + *loc) ;
					loc++;

					std::vector<Feedback3DColor> verts ;

					for(int i=0;i<nvertices;++i)
						verts.push_back(Feedback3DColor(loc)),loc+=Feedback3DColor::sizeInBuffer() ;

					Polygone *P = new Polygone(verts) ;

					primitive_tab.push_back(ParserUtils::checkPolygon(P)) ;

					if(P == NULL)
						nb_degenerated_polys++ ;

					nb_polys++ ;
				}
				break ;

			case GL_POINT_TOKEN:
				{
					Point *Pt = new Point(Feedback3DColor(loc)) ;

					primitive_tab.push_back(Pt);//ParserUtils::checkPoint(Pt)) ;

					if(Pt == NULL)
						nb_degenerated_points++ ;

					nb_points++ ;
					loc += Feedback3DColor::sizeInBuffer();
				}
				break;
			default:
				break;
		}
	}

}
示例#5
0
void vrender::VectorialRender(RenderCB render_callback, void *callback_params, VRenderParams& vparams)
{
    GLfloat *feedbackBuffer = NULL ;
    SortMethod *sort_method = NULL ;
    Exporter *exporter = NULL ;

    try
    {
        GLint returned = -1 ;

        vparams.error() = 0 ;

        int nb_renders = 0 ;

        vparams.progress(0.0, QGLViewer::tr("Rendering...")) ;

        while(returned < 0)
        {
            if(feedbackBuffer != NULL)
                delete[] feedbackBuffer ;

            feedbackBuffer = new GLfloat[vparams.size()] ;

            if(feedbackBuffer == NULL)
                throw std::runtime_error("Out of memory during feedback buffer allocation.") ;

            glFeedbackBuffer(vparams.size(), GL_3D_COLOR, feedbackBuffer);
            glRenderMode(GL_FEEDBACK);
            render_callback(callback_params);
            returned = glRenderMode(GL_RENDER);

            nb_renders++ ;

            if(returned < 0)
                vparams.size() *= 2 ;
        }

#ifdef A_VOIR
        if(SortMethod != EPS_DONT_SORT)
        {
            GLint depth_bits ;
            glGetIntegerv(GL_DEPTH_BITS, &depth_bits) ;

            EGALITY_EPS 		= 2.0/(1 << depth_bits) ;
            LINE_EGALITY_EPS 	= 2.0/(1 << depth_bits) ;
        }
#endif
        if (returned > vparams.size())
            vparams.size() = returned;
#ifdef _VRENDER_DEBUG
        cout << "Size = " << vparams.size() << ", returned=" << returned << endl ;
#endif

        //  On a un beau feedback buffer tout plein de saloperies. Faut aller
        // defricher tout ca. Ouaiiiis !

        vector<PtrPrimitive> primitive_tab ;

        ParserGL parserGL ;
        parserGL.parseFeedbackBuffer(feedbackBuffer,returned,primitive_tab,vparams) ;

        if(feedbackBuffer != NULL)
        {
            delete[] feedbackBuffer ;
            feedbackBuffer = NULL ;
        }

        if(vparams.isEnabled(VRenderParams::OptimizeBackFaceCulling))
        {
            BackFaceCullingOptimizer bfopt ;
            bfopt.optimize(primitive_tab,vparams) ;
        }

        // Lance la methode de sorting

        switch(vparams.sortMethod())
        {
        case VRenderParams::AdvancedTopologicalSort:
        case VRenderParams::TopologicalSort: {
            TopologicalSortMethod *tsm = new TopologicalSortMethod() ;
            tsm->setBreakCycles(vparams.sortMethod() == VRenderParams::AdvancedTopologicalSort) ;
            sort_method = tsm ;
        }
        break ;

        case VRenderParams::BSPSort:
            sort_method = new BSPSortMethod() ;
            break ;

        case VRenderParams::NoSorting:
            sort_method = new DontSortMethod() ;
            break ;
        default:
            throw std::runtime_error("Unknown sorting method.") ;
        }

        sort_method->sortPrimitives(primitive_tab,vparams) ;

        // Lance les optimisations. L'ordre est important.

        if(vparams.isEnabled(VRenderParams::CullHiddenFaces))
        {
            VisibilityOptimizer vopt ;
            vopt.optimize(primitive_tab,vparams) ;
        }

#ifdef A_FAIRE
        if(vparams.isEnabled(VRenderParams::OptimizePrimitiveSplit))
        {
            PrimitiveSplitOptimizer psopt ;
            psopt.optimize(primitive_tab) ;
        }
#endif
        // Ecrit le fichier

        switch(vparams.format())
        {
        case VRenderParams::EPS:
            exporter = new EPSExporter() ;
            break ;
        case VRenderParams::PS:
            exporter = new PSExporter() ;
            break ;
        case VRenderParams::XFIG:
            exporter = new FIGExporter() ;
            break ;
#ifdef A_FAIRE
        case VRenderParams::SVG:
            exporter = new SVGExporter() ;
            break ;
#endif
        default:
            throw std::runtime_error("Sorry, this output format is not handled now. Only EPS and PS are currently supported.") ;
        }

        // sets background and black & white options

        GLfloat viewport[4],clearColor[4],lineWidth,pointSize ;

        glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
        glGetFloatv(GL_LINE_WIDTH, &lineWidth);
        glGetFloatv(GL_POINT_SIZE, &pointSize);
        glGetFloatv(GL_VIEWPORT, viewport);

        lineWidth /= (float)max(viewport[2] - viewport[0],viewport[3]-viewport[1]) ;

        // Sets which bounding box to use.

        if(vparams.isEnabled(VRenderParams::TightenBoundingBox))
            exporter->setBoundingBox(parserGL.xmin(),parserGL.ymin(),parserGL.xmax(),parserGL.ymax()) ;
        else
            exporter->setBoundingBox(viewport[0],viewport[1],viewport[0]+viewport[2],viewport[1]+viewport[3]) ;

        exporter->setBlackAndWhite(vparams.isEnabled(VRenderParams::RenderBlackAndWhite)) ;
        exporter->setClearBackground(vparams.isEnabled(VRenderParams::AddBackground)) ;
        exporter->setClearColor(clearColor[0],clearColor[1],clearColor[2]) ;

        exporter->exportToFile(vparams.filename(),primitive_tab,vparams) ;

        // deletes primitives

        for(unsigned int i=0; i<primitive_tab.size(); ++i)
            delete primitive_tab[i] ;

        if(exporter != NULL) delete exporter ;
        if(sort_method != NULL) delete sort_method ;
    }
    catch(exception& e)
    {
        cout << "Render aborted: " << e.what() << endl ;

        if(exporter != NULL) delete exporter ;
        if(sort_method != NULL) delete sort_method ;
        if(feedbackBuffer != NULL) delete[] feedbackBuffer ;

        throw e ;
    }
}
void TopologicalSortUtils::recursTopologicalSort(	vector< vector<int> >& precedence_graph,
																	vector<PtrPrimitive>& primitive_tab,
																	vector<bool>& already_rendered,
																	vector<bool>& already_visited,
																	vector<PtrPrimitive>& new_pr_tab,
																	int indx,
																	vector<int>& ancestors,
																	int& ancestors_backward_index,
																	int& nb_cycles,
																	VRenderParams& vparams,
																	int info_cnt,int& nbrendered)
{
	// One must first render the primitives indicated by the precedence graph,
	// then render the current primitive. Skews are detected, but and treated.

	already_visited[indx] = true ;
	ancestors.push_back(indx) ;

	for(unsigned int j=0;j<precedence_graph[indx].size();++j)
	{
		// Both tests are important. If we ommit the second one, the recursion is
		// always performed down to the next cycle, although this is useless if
		// the current primitive was rendered already.

		if(!already_visited[precedence_graph[indx][j]])
		{
			if(!already_rendered[precedence_graph[indx][j]])
			{
				recursTopologicalSort(	precedence_graph,primitive_tab,already_rendered,already_visited,
												new_pr_tab,precedence_graph[indx][j],ancestors,ancestors_backward_index,nb_cycles,vparams,info_cnt,nbrendered) ;

				if(ancestors_backward_index != INT_MAX && ancestors.size() > (unsigned int)(ancestors_backward_index+1))
				{
#ifdef DEBUG_TS
					cout << "Returning early" << endl ;
#endif
					already_visited[indx] = false ;
					ancestors.pop_back() ;
					return;
				}
				if(ancestors_backward_index != INT_MAX) // we are returning from emergency. j must be re-tried
					--j ;
			}
		}
		else
		{
			//  A cycle is detected. It must be broken. The algorithm is the following: primitives of the cycle
			// are successively split by a chosen splitting plane and the precendence graph is updated
			// at the same time by re-computing primitive precedence. As soon as the cycle is broken,
			// the algorithm stops and calls recursively calls on the new precedence graph. This necessarily
			// happens because of the BSP-node nature of the current set of primitives.

			// 0 - stats
			++nb_cycles ;

			// 0.5 - determine cycle beginning

			int cycle_beginning_index = -1 ;
                        for(int i=(int)(ancestors.size())-1; i >= 0 && cycle_beginning_index < 0;--i)
				if(ancestors[i] == precedence_graph[indx][j])
					cycle_beginning_index = i ;
#ifdef DEBUG_TS
			cout << "Unbreaking cycle : " ;
			for(unsigned int i=0;i<ancestors.size();++i)
				cout << ancestors[i] << " " ;
			cout << precedence_graph[indx][j] << endl ;
#endif
#ifdef DEBUG_TS
			assert(cycle_beginning_index >= 0) ;
#endif
			// 1 - determine splitting plane

			int split_prim_ancestor_indx = -1 ;
			int split_prim_indx = -1 ;

			// Go down ancestors tab, starting from the skewing primitive, and stopping at it.

			for(unsigned int i2=cycle_beginning_index;i2<ancestors.size() && split_prim_ancestor_indx < 0;++i2)
				if(primitive_tab[ancestors[i2]]->nbVertices() > 2)
				{
					split_prim_ancestor_indx = i2 ;
					split_prim_indx = ancestors[i2] ;
				}

#ifdef DEBUG_TS
			cout << "Split primitive index = " << split_prim_ancestor_indx << "(primitive = " << split_prim_indx << ")" << endl ;
#endif
			if(split_prim_indx < 0)	// no need to unskew cycles between segments and points
				continue ;

			// 2 - split all necessary primitives

			const Polygone *P = dynamic_cast<const Polygone *>(primitive_tab[split_prim_indx]) ;
			const NVector3& normal = NVector3(P->normal()) ;
			double c(P->c()) ;
			ancestors.push_back(precedence_graph[indx][j]) ;				// sentinel
			ancestors.push_back(ancestors[cycle_beginning_index+1]) ;	// sentinel
			bool cycle_broken = false ;

			for(unsigned int i3=cycle_beginning_index+1;i3<ancestors.size()-1 && !cycle_broken;++i3)
				if(ancestors[i3] != split_prim_indx)
				{
					bool prim_lower_ante_contains_im1 = false ;
					bool prim_upper_ante_contains_im1 = false ;
					bool prim_lower_prec_contains_ip1 = false ;
					bool prim_upper_prec_contains_ip1 = false ;

					Primitive *prim_upper = NULL ;
					Primitive *prim_lower = NULL ;

					PrimitivePositioning::splitPrimitive(primitive_tab[ancestors[i3]],normal,c,prim_upper,prim_lower) ;

					if(prim_upper == NULL || prim_lower == NULL)
						continue ;
#ifdef DEBUG_TS
					cout << "Splitted primitive " << ancestors[i3] << endl ;
#endif

					vector<int> prim_upper_prec ;
					vector<int> prim_lower_prec ;

					vector<int> old_prec = vector<int>(precedence_graph[ancestors[i3]]) ;

                                        unsigned int upper_indx = precedence_graph.size() ;
					int lower_indx = ancestors[i3] ;

					//  Updates the precedence graph downwards.

					for(unsigned int k=0;k<old_prec.size();++k)
					{
						int prp1 = PrimitivePositioning::computeRelativePosition(prim_upper,primitive_tab[old_prec[k]]) ;
#ifdef DEBUG_TS
						cout << "Compariing " << upper_indx << " and " << old_prec[k] << ": " ;
#endif
						// It can not be Upper, because it was lower from the original primitive, but it is not
						// necessary lower any longer because of the split.

						if(prp1 & PrimitivePositioning::Lower)
						{
#ifdef DEBUG_TS
							cout << " > " << endl ;
#endif
							prim_upper_prec.push_back(old_prec[k]) ;

							if(old_prec[k] == ancestors[i3+1])
								prim_upper_prec_contains_ip1 = true ;
						}
#ifdef DEBUG_TS
						else
							cout << " I " << endl ;
#endif

						int prp2 = PrimitivePositioning::computeRelativePosition(prim_lower,primitive_tab[old_prec[k]]) ;
#ifdef DEBUG_TS
						cout << "Compariing " << lower_indx << " and " << old_prec[k] << ": " ;
#endif
						if(prp2 & PrimitivePositioning::Lower)
						{
#ifdef DEBUG_TS
							cout << " > " << endl ;
#endif
							prim_lower_prec.push_back(old_prec[k]) ;

							if(old_prec[k] == ancestors[i3+1])
								prim_lower_prec_contains_ip1 = true ;
						}
#ifdef DEBUG_TS
						else
							cout << " I " << endl ;
#endif
					}

					// We also have to update the primitives which are upper to the
					// current one, because some of them may not be upper anymore.
					// This would requires either a O(n^2) algorithm, or to store an
					// dual precedence graph. For now it's O(n^2). This test can not
					// be skipped because upper can still be lower to ancestors[i-1].

					for(unsigned int l=0;l<precedence_graph.size();++l)
						if(l != (unsigned int)lower_indx)
							for(int k=0;k<(int)precedence_graph[l].size();++k)
								if(precedence_graph[l][k] == ancestors[i3])
								{
									int prp1 = PrimitivePositioning::computeRelativePosition(prim_upper,primitive_tab[l]) ;

									// It can not be Lower, because it was upper from the original primitive, but it is not
									// necessary upper any longer because of the split.

									if(prp1 & PrimitivePositioning::Upper)
									{
										// Still upper. Add the new index at end of the array

										precedence_graph[l].push_back(upper_indx) ;

										if(l == (unsigned int)ancestors[i3-1])
											prim_upper_ante_contains_im1 = true ;
									}
									// If the primitive is not upper anymore there is
									// nothing to change since the index has changed.

									int prp2 = PrimitivePositioning::computeRelativePosition(prim_lower,primitive_tab[l]) ;
#ifdef DEBUG_TS
									cout << "Compariing " << l << " and " << lower_indx  << ": " ;
#endif
									if(prp2 & PrimitivePositioning::Upper)
									{
#ifdef DEBUG_TS
										cout << " > " << endl ;
#endif
										if(l == (unsigned int)ancestors[i3-1])						 // The index is the same => nothing to change.
											prim_lower_ante_contains_im1 = true ;
									}
									else
									{
#ifdef DEBUG_TS
										cout << " I " << endl ;
#endif
										// Not upper anymore. We have to suppress this entry from the tab.

										precedence_graph[l][k] = precedence_graph[l][precedence_graph[l].size()-1] ;
										precedence_graph[l].pop_back() ;
										--k ;
									}

									break ;	// each entry is present only once.
								}

					// setup recorded new info

					primitive_tab.push_back(prim_upper) ;
					delete primitive_tab[lower_indx] ;
					primitive_tab[lower_indx] = prim_lower ;

					// Adds the info to the precedence graph

					precedence_graph.push_back(prim_upper_prec) ;
					precedence_graph[lower_indx] = prim_lower_prec ;

					// Adds new entries to the 'already_rendered' and 'already_visited' vectors
					already_visited.push_back(false) ;
					already_rendered.push_back(false) ;
#ifdef DEBUG_TS
					cout << "New precedence graph: " << endl ;
					printPrecedenceGraph(precedence_graph,primitive_tab) ;
#endif
					// Checks if the cycle is broken. Because the graph is only
					// updated downwards, we check wether lower (which still is
					// lower to ancestors[i-1]) is upper to ancestors[i+1], or
					// if upper is still .

					if(( !(prim_lower_ante_contains_im1 && prim_lower_prec_contains_ip1))
					  &&(!(prim_upper_ante_contains_im1 && prim_upper_prec_contains_ip1)))
						cycle_broken = true ;
				}

			ancestors.pop_back() ;
			ancestors.pop_back() ;

			// 3 - recurs call

			if(cycle_broken)
			{
				ancestors_backward_index = cycle_beginning_index ;
#ifdef DEBUG_TS
				cout << "Cycle broken. Jumping back to rank " << ancestors_backward_index << endl ;
#endif
				already_visited[indx] = false ;
				ancestors.pop_back() ;
				return;
			}
#ifdef DEBUG_TS
			else
				cout << "Cycle could not be broken !!" << endl ;
#endif
		}
	}

	if(!already_rendered[indx])
	{
#ifdef DEBUG_TS
		cout << "Returning ok. Rendered primitive " << indx << endl ;
#endif
		new_pr_tab.push_back(primitive_tab[indx]) ;

		if((++nbrendered)%info_cnt==0)
			vparams.progress(nbrendered/(float)primitive_tab.size(), QGLViewer::tr("Advanced topological sort")) ;
	}

	already_rendered[indx] = true ;
	ancestors_backward_index = INT_MAX ;
	already_visited[indx] = false ;
	ancestors.pop_back() ;
}
void VisibilityOptimizer::optimize(vector<PtrPrimitive>& primitives,VRenderParams& vparams)
#endif
{
#ifdef DEBUG_VO
	cout << "Optimizing visibility." << endl ;
#endif
	int N = primitives.size()/200 + 1 ;

#ifdef DEBUG_EPSRENDER__SHOW1
//	cout << "Showing viewer." << endl ;
//	myViewer viewer ;
//	viewer.show();
	double minx =  FLT_MAX ;
	double miny =  FLT_MAX ;
	double maxx = -FLT_MAX ;
	double maxy = -FLT_MAX ;
	for(unsigned int i=0;i<primitives.size();++i)
		for(int j=0;j<primitives[i]->nbVertices();++j)
		{
			if(maxx < primitives[i]->vertex(j).x()) maxx = primitives[i]->vertex(j).x() ;
			if(maxy < primitives[i]->vertex(j).y()) maxy = primitives[i]->vertex(j).y() ;
			if(minx > primitives[i]->vertex(j).x()) minx = primitives[i]->vertex(j).x() ;
			if(miny > primitives[i]->vertex(j).y()) miny = primitives[i]->vertex(j).y() ;
		}

	glMatrixMode(GL_PROJECTION) ;
	glLoadIdentity() ;
	glOrtho(minx,maxx,miny,maxy,-1,1) ;
	glMatrixMode(GL_MODELVIEW) ;
	glLoadIdentity() ;

	cout << "Window set to " << minx << " " << maxx << " " << miny << " " << maxy << endl ;
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) ;
	glLineWidth(3.0) ;
#endif

	int nb_culled = 0 ;

	// Ca serait pas mal mieux avec une interface c++...

	gpc_polygon cumulated_union ;
	cumulated_union.num_contours = 0 ;
	cumulated_union.hole = NULL ;
	cumulated_union.contour = NULL ;
	int nboptimised = 0 ;

	for(int pindex = primitives.size() - 1; pindex >= 0;--pindex,++nboptimised)
		if(primitives[pindex] != NULL)
		{
#ifdef A_FAIRE
			percentage_finished = pindex / (float)primitives.size() ;
#endif

			if(primitives[pindex]->nbVertices() > 1)
			{
#ifdef DEBUG_VO
				if(pindex%50==0)
				{
					char buff[500] ;
					sprintf(buff,"Left: % 6ld - Culled: % 6ld", (long)pindex,(long)nb_culled) ;
					fprintf(stdout,buff);

					for(unsigned int j=0;j<strlen(buff);++j)
						fprintf(stdout,"\b") ;

					fflush(stdout) ;
				}
#endif

				try
				{
					PtrPrimitive p(primitives[pindex]) ;
					gpc_polygon difference ;
					gpc_polygon new_poly ;
					gpc_polygon new_poly_reduced ;
					new_poly.num_contours = 0 ;
					new_poly.hole = NULL ;
					new_poly.contour = NULL ;
					new_poly_reduced.num_contours = 0 ;
					new_poly_reduced.hole = NULL ;
					new_poly_reduced.contour = NULL ;

					// 1 - creates a gpc_polygon corresponding to the current primitive

					gpc_vertex_list *new_poly_verts = new gpc_vertex_list ;
					gpc_vertex_list *new_poly_reduced_verts = new gpc_vertex_list ;

					double mx = 0.0 ;
					double my = 0.0 ;

					if(p->nbVertices() == 2)
					{
						new_poly_verts->num_vertices = 4 ;
						new_poly_verts->vertex = new gpc_vertex[4] ;
						new_poly_reduced_verts->num_vertices = 4 ;
						new_poly_reduced_verts->vertex = new gpc_vertex[4] ;

						double deps = 0.001 ;
						double du = p->vertex(1).y()-p->vertex(0).y() ;
						double dv = p->vertex(1).x()-p->vertex(0).x() ;
						double n = sqrt(du*du+dv*dv) ;
						du *= deps/n ;
						dv *= deps/n ;
						new_poly_verts->vertex[0].x = p->vertex(0).x() + du ;
						new_poly_verts->vertex[0].y = p->vertex(0).y() + dv ;
						new_poly_verts->vertex[1].x = p->vertex(1).x() + du ;
						new_poly_verts->vertex[1].y = p->vertex(1).y() + dv ;
						new_poly_verts->vertex[2].x = p->vertex(1).x() - du ;
						new_poly_verts->vertex[2].y = p->vertex(1).y() - dv ;
						new_poly_verts->vertex[3].x = p->vertex(0).x() - du ;
						new_poly_verts->vertex[3].y = p->vertex(0).y() - dv ;

						new_poly_reduced_verts->vertex[0].x = p->vertex(0).x() + du ;
						new_poly_reduced_verts->vertex[0].y = p->vertex(0).y() + dv ;
						new_poly_reduced_verts->vertex[1].x = p->vertex(1).x() + du ;
						new_poly_reduced_verts->vertex[1].y = p->vertex(1).y() + dv ;
						new_poly_reduced_verts->vertex[2].x = p->vertex(1).x() - du ;
						new_poly_reduced_verts->vertex[2].y = p->vertex(1).y() - dv ;
						new_poly_reduced_verts->vertex[3].x = p->vertex(0).x() - du ;
						new_poly_reduced_verts->vertex[3].y = p->vertex(0).y() - dv ;
					}
					else
					{
						new_poly_verts->num_vertices = p->nbVertices() ;
						new_poly_verts->vertex = new gpc_vertex[p->nbVertices()] ;

						for(int i=0;i<p->nbVertices();++i)
						{
							new_poly_verts->vertex[i].x = p->vertex(i).x() ;
							new_poly_verts->vertex[i].y = p->vertex(i).y() ;
							mx += p->vertex(i).x() ;
							my += p->vertex(i).y() ;
						}
						mx /= p->nbVertices() ;
						my /= p->nbVertices() ;

						new_poly_reduced_verts->num_vertices = p->nbVertices() ;
						new_poly_reduced_verts->vertex = new gpc_vertex[p->nbVertices()] ;

						for(int j=0;j<p->nbVertices();++j)
						{
							new_poly_reduced_verts->vertex[j].x = mx + (p->vertex(j).x() - mx)*0.999 ;
							new_poly_reduced_verts->vertex[j].y = my + (p->vertex(j).y() - my)*0.999 ;
						}
					}
					gpc_add_contour(&new_poly,new_poly_verts,false) ;
					gpc_add_contour(&new_poly_reduced,new_poly_reduced_verts,false) ;

					// 2 - computes the difference between this polygon, and the union of the
					// 	preceeding ones.

					gpc_polygon_clip(GPC_DIFF,&new_poly_reduced,&cumulated_union,&difference) ;

					// 3 - checks the difference. If void, the primitive is not visible: skip it
					// 	and go to next primitive.

					if(difference.num_contours == 0)
					{
						++nb_culled ;
						delete p ;
						primitives[pindex] = NULL ;
						continue ;
					}

					// 4 - The primitive is visible. Let's add it to the cumulated union of
					// 	primitives.

					if(p->nbVertices() > 2)
					{
						gpc_polygon cumulated_union_tmp ;
						cumulated_union_tmp.num_contours = 0 ;
						cumulated_union_tmp.hole = NULL ;
						cumulated_union_tmp.contour = NULL ;

						gpc_polygon_clip(GPC_UNION,&new_poly,&cumulated_union,&cumulated_union_tmp) ;

						gpc_free_polygon(&cumulated_union) ;
						cumulated_union = cumulated_union_tmp ;
					}

					gpc_free_polygon(&new_poly) ;
					gpc_free_polygon(&new_poly_reduced) ;
					gpc_free_polygon(&difference) ;
#ifdef DEBUG_EPSRENDER__SHOW1
					glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) ;

					glColor3f(1.0,0.0,0.0) ;

					for(int i=0;i<cumulated_union.num_contours;++i)
					{
						glBegin(GL_LINE_LOOP) ;
						for(int j=0;j<cumulated_union.contour[i].num_vertices;++j)
							glVertex2f(cumulated_union.contour[i].vertex[j].x,cumulated_union.contour[i].vertex[j].y) ;
						glEnd() ;
					}

					glFlush() ;
					glXSwapBuffers(glXGetCurrentDisplay(),glXGetCurrentDrawable()) ;
#endif
				}
				catch(exception& )
				{
					; // std::cout << "Could not treat primitive " << pindex << ": internal gpc error." << endl ;
				}
			}

			if(nboptimised%N==0)
				vparams.progress(nboptimised/(float)primitives.size(), QGLViewer::tr("Visibility optimization")) ;
		}

#ifdef DEBUG_VO
	cout << nb_culled << " primitives culled over " << primitives.size() << "." << endl ;
#endif

	gpc_free_polygon(&cumulated_union) ;
}