Edit

kc3-lang/ftgl/src/FTVectoriser.cpp

Branch :

  • Show log

    Commit

  • Author : henry
    Date : 2001-11-12 02:44:18
    Hash : 86b5c356
    Message : gluTesselation now feeds data to internal FTMesh class

  • src/FTVectoriser.cpp
  • #include	"FTVectoriser.h"
    #include	"FTGL.h"
    
    
    #ifndef CALLBACK
    #define CALLBACK
    #endif
    
    
    
    void CALLBACK ftglError( GLenum errCode, FTMesh* mesh)
    {
    	mesh->Error( errCode);
    }
    
    void CALLBACK ftglVertex( void* data, FTMesh* mesh)
    {
    	double* vertex = (double*)data;
    	mesh->AddPoint( vertex[0], vertex[1], vertex[2]);
    }
    
    
    void CALLBACK ftglBegin( GLenum type, FTMesh* mesh)
    {
    	mesh->Begin( type);
    }
    
    
    void CALLBACK ftglEnd( FTMesh* mesh)
    {
    	mesh->End();
    }
    
    
    void CALLBACK ftglCombine( GLdouble coords[3], void* vertex_data[4], GLfloat weight[4], void** outData, FTMesh* mesh)
    {
    	double* vertex = (double*)coords;
    	mesh->tempPool.push_back( ftPoint( vertex[0], vertex[1], vertex[2]));
    	
    	*outData = &mesh->tempPool[ mesh->tempPool.size() - 1].x;
    }
    
    
    //=============================================================================
    
    bool operator == ( const ftPoint &a, const ftPoint &b) 
    {
    	return((a.x == b.x) && (a.y == b.y) && (a.z == b.z));
    }
    
    bool operator != ( const ftPoint &a, const ftPoint &b) 
    {
    	return((a.x != b.x) || (a.y != b.y) || (a.z != b.z));
    }
    		
    
    FTMesh::FTMesh()
    :	err(0)
    {
    	tess.reserve( 16);
    	tempPool.reserve( 128);
    }
    
    
    FTMesh::~FTMesh()
    {
    	for( int t = 0; t < tess.size(); ++t)
    	{
    		delete tess[t];
    	}
    	tess.clear();
    
    	tempPool.clear();
    }
    
    
    void FTMesh::AddPoint( const double x, const double y, const double z)
    {
    	tempTess->AddPoint( x, y, z);
    }
    
    void FTMesh::Begin( GLenum m)
    {
    	tempTess = new FTTesselation;
    	tempTess->meshType = m;
    }
    
    
    void FTMesh::End()
    {
    	tess.push_back( tempTess);
    }
    
    
    double* FTMesh::Point()
    {
    	return &tempTess->pointList[ tempTess->size() - 1].x;
    
    }
    
    
    int FTMesh::size() const
    {
    	int s = 0;
    	for( int t = 0; t < tess.size(); ++t)
    	{
    		s += tess[t]->size();
    		++s;
    	}
    	return s;
    }
    
    
    //=============================================================================
    
    
    FTVectoriser::FTVectoriser( const FT_Glyph glyph)
    :	contour(0),
    	mesh(0),
    	contourFlag(0),
    	kBSTEPSIZE( 0.2f)
    {
    	FT_OutlineGlyph outline = (FT_OutlineGlyph)glyph;
    	ftOutline = outline->outline;
    	
    	contourList.reserve( ftOutline.n_contours);
    }
    
    
    FTVectoriser::~FTVectoriser()
    {
    	for( int c = 0; c < contours(); ++c)
    	{
    		delete contourList[c];
    	}
    
    	contourList.clear();
    	
    	if( mesh)
    		delete mesh;
    }
    
    
    int FTVectoriser::points()
    {
    	int s = 0;
    	for( int c = 0; c < contours(); ++c)
    	{
    		s += contourList[c]->size();
    	}
    	
    	return s;
    }
    
    
    bool FTVectoriser::Process()
    {
    	short first = 0;
    	short last;
    	const short cont = ftOutline.n_contours;
    	
    	for( short c = 0; c < cont; ++c)
    	{
    		contour = new FTContour;
    		contourFlag = ftOutline.flags;
    		last = ftOutline.contours[c];
    
    		for( int p = first; p <= last; ++p)
    		{
    			switch( ftOutline.tags[p])
    			{
    				case FT_Curve_Tag_Conic:
    					p += Conic( p, first, last);
    					break;
    				case FT_Curve_Tag_Cubic:
    					p += Cubic( p, first, last);
    					break;
    				case FT_Curve_Tag_On:
    				default:
     					contour->AddPoint( ftOutline.points[p].x, ftOutline.points[p].y);
    			}
    		}
    		
    		contourList.push_back( contour);
    		first = last + 1;
    	}
    	
    	return true;
    }
    
    
    int FTVectoriser::Conic( const int index, const int first, const int last)
    {
    	int next = index + 1;
    	int prev = index - 1;
    	
    	if( index == last)
    		next = first; 
    	
    	if( index == first)
    		prev = last; 
    	
    	if( ftOutline.tags[next] != FT_Curve_Tag_Conic)
    	{
    		ctrlPtArray[0][0] = ftOutline.points[prev].x;	ctrlPtArray[0][1] = ftOutline.points[prev].y;
    		ctrlPtArray[1][0] = ftOutline.points[index].x;	ctrlPtArray[1][1] = ftOutline.points[index].y;
    		ctrlPtArray[2][0] = ftOutline.points[next].x;	ctrlPtArray[2][1] = ftOutline.points[next].y;
    		
    		evaluateCurve( 2);
    		return 1;
    	}
    	else
    	{
    		int next2 = next + 1;
    		if( next == last)
    			next2 = first;
    		
    		//create a phantom point
    		float x = ( ftOutline.points[index].x + ftOutline.points[next].x) / 2;
    		float y = ( ftOutline.points[index].y + ftOutline.points[next].y) / 2;
    		
    		// process first curve
    		ctrlPtArray[0][0] = ftOutline.points[prev].x;	ctrlPtArray[0][1] = ftOutline.points[prev].y;
    		ctrlPtArray[1][0] = ftOutline.points[index].x;	ctrlPtArray[1][1] = ftOutline.points[index].y;
    		ctrlPtArray[2][0] = x;							ctrlPtArray[2][1] = y;
    		
    		evaluateCurve( 2);
    		
    		// process second curve
    		ctrlPtArray[0][0] = x;							ctrlPtArray[0][1] = y;
    		ctrlPtArray[1][0] = ftOutline.points[next].x;	ctrlPtArray[1][1] = ftOutline.points[next].y;
    		ctrlPtArray[2][0] = ftOutline.points[next2].x;	ctrlPtArray[2][1] = ftOutline.points[next2].y;
    		evaluateCurve( 2);
    		
    		return 2;
    	}
    }
    
    
    int FTVectoriser::Cubic( const int index, const int first, const int last)
    {
    	int next = index + 1;
    	int prev = index - 1;
    	
    	if( index == last)
    		next = first; 
    	
    	int next2 = next + 1;
    	
    	if( next == last)
    		next2 = first;
    	
    	if( index == first)
    		prev = last; 
    
    	ctrlPtArray[0][0] = ftOutline.points[prev].x;		ctrlPtArray[0][1] = ftOutline.points[prev].y;
    	ctrlPtArray[1][0] = ftOutline.points[index].x;		ctrlPtArray[1][1] = ftOutline.points[index].y;
    	ctrlPtArray[2][0] = ftOutline.points[next].x;		ctrlPtArray[2][1] = ftOutline.points[next].y;
    	ctrlPtArray[3][0] = ftOutline.points[next2].x;		ctrlPtArray[3][1] = ftOutline.points[next2].y;
    
    	evaluateCurve( 3);
    	return 2;
    }
    
    
    // De Casteljau algorithm contributed by Jed Soane
    void FTVectoriser::deCasteljau( const float t, const int n)
    {
        //Calculating successive b(i)'s using de Casteljau algorithm.
        for( int i = 1; i <= n; i++)
            for( int k = 0; k <= (n - i); k++)
            {
    			bValues[i][k][0] = (1 - t) * bValues[i - 1][k][0] + t * bValues[i - 1][k + 1][0];
    			bValues[i][k][1] = (1 - t) * bValues[i - 1][k][1] + t * bValues[i - 1][k + 1][1];
    		}
    		
        //Specify next vertex to be included on curve
    	contour->AddPoint( bValues[n][0][0], bValues[n][0][1]);
    }
    
    
    // De Casteljau algorithm contributed by Jed Soane
    void FTVectoriser::evaluateCurve( const int n)
    {
        // setting the b(0) equal to the control points
        for( int i = 0; i <= n; i++)
    	{
    		bValues[0][i][0] = ctrlPtArray[i][0];
    		bValues[0][i][1] = ctrlPtArray[i][1];
        }
    
        float t;            					//parameter for curve point calc. [0.0, 1.0]
    
        for( int m = 0; m <= ( 1 / kBSTEPSIZE); m++)
        {
        	t = m * kBSTEPSIZE;
            deCasteljau( t, n);  //calls to evaluate point on curve att.
        }
    }
    
    
    void FTVectoriser::GetOutline( double* data)
    {
    	int i = 0;
    	
    	for( int c= 0; c < contours(); ++c)
    	{
    		const FTContour* contour = contourList[c];
    		
    		for( int p = 0; p < contour->size(); ++p)
    		{
    			data[i] = static_cast<double>(contour->pointList[p].x / 64.0f); // is 64 correct?
    			data[i + 1] = static_cast<double>(contour->pointList[p].y / 64.0f);
    			data[i + 2] = 0.0; // static_cast<double>(contour->pointList[p].z / 64.0f);
    			i += 3;
    		}
    	}
    }
    
    
    void FTVectoriser::MakeMesh( int zNormal)
    {
    	if( mesh)
    	{
    		delete mesh;
    	}
    		
    	mesh = new FTMesh;
    	
    	GLUtesselator* tobj = gluNewTess();
    	
    	gluTessCallback( tobj, GLU_TESS_BEGIN_DATA,		(void (CALLBACK*)())ftglBegin);
    	gluTessCallback( tobj, GLU_TESS_VERTEX_DATA,	(void (CALLBACK*)())ftglVertex);
    	gluTessCallback( tobj, GLU_TESS_COMBINE_DATA,	(void (CALLBACK*)())ftglCombine);
    	gluTessCallback( tobj, GLU_TESS_END_DATA,		(void (CALLBACK*)())ftglEnd);
    	gluTessCallback( tobj, GLU_TESS_ERROR_DATA,		(void (CALLBACK*)())ftglError);
    	
    	
    	if( contourFlag & ft_outline_even_odd_fill) // ft_outline_reverse_fill
    	{
    		gluTessProperty( tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
    	}
    	else
    	{
    		gluTessProperty( tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
    	}
    	
    	
    	gluTessProperty( tobj, GLU_TESS_TOLERANCE, 0);
    	gluTessNormal( tobj, 0.0, 0.0, zNormal);
    	gluTessBeginPolygon( tobj, mesh);
    	
    		for( int c = 0; c < contours(); ++c)
    		{
    			const FTContour* contour = contourList[c];
    			gluTessBeginContour( tobj);
    			
    				for( int p = 0; p < contour->size(); ++p)
    				{
    					double* d = const_cast<double*>(&contour->pointList[p].x);
    					gluTessVertex( tobj, d, d);
    				}
    			gluTessEndContour( tobj);
    		}
    		
    	gluTessEndPolygon( tobj);
    
    	gluDeleteTess( tobj);
    	
    }
    
    
    void FTVectoriser::GetMesh( double* data)
    {
     	// Now write it out
    	int i = 0;
    	
    	// fill out the header
    	int msize = mesh->tess.size();
    	data[0] = msize;
    	
    	for( int p = 0; p < data[0]; ++p)
    	{
    		FTTesselation* tess = mesh->tess[p];
    		int tSize =  tess->pointList.size();
    		int tType =  tess->meshType;
    		
    		data[i+1] = tType;
    		data[i+2] = tSize;
    		i += 3;
    		for( int q = 0; q < ( tess->pointList.size()); ++q)
    		{
    			data[i] = tess->pointList[q].x / 64.0f; // is 64 correct?
    			data[i + 1] = tess->pointList[q].y / 64.0f;
    			data[i + 2] = 0.0; // static_cast<double>(mesh->pointList[p].z / 64.0f);
    			i += 3;
    		
    		}
    
    	}
    
    }