c++openglglutesselation

Force GLUtesselator to generate only GL_TRIANGLES?


It's pretty hard to render the data I generate, if I want to use one vertex array format only.

I tried to provide GLU_TESS_EDGE_FLAG_DATA callback, but it made my program crash. (also tried without "_DATA" in end, same effect).

How can I get it to generate GL_TRIANGLES only?


Solution

  • gluTessCallback(), GLU_TESS_EDGE_FLAG:

    ... if a non-NULL edge flag callback is provided ... fans and strips are converted to independent triangles.

    This is what I've been using:

    struct TessContext
    {
        ~TessContext()
        {
            for( size_t i = 0; i < combined.size(); ++i )
            {
                delete[] combined[i];
            }
        }
    
        typedef std::pair< double, double > Point;
        std::vector< Point > pts;
        std::vector< GLdouble* > combined;
    };
    
    #ifndef CALLBACK
    #define CALLBACK __stdcall
    #endif
    
    void CALLBACK tess_begin( GLenum ) {}
    void CALLBACK tess_edgeFlag( GLboolean ) {}
    void CALLBACK tess_end() {}
    
    void CALLBACK tess_vertex
        ( 
        void*           data, 
        TessContext*    ctx 
        )
    {
        GLdouble* coord = (GLdouble*)data;
        ctx->pts.push_back( TessContext::Point( coord[0], coord[1] ) );
    }
    
    void CALLBACK tess_combine
        ( 
        GLdouble        coords[3], 
        void*           vertex_data[4], 
        GLfloat         weight[4], 
        void**          outData, 
        TessContext*    ctx 
        )
    {
        GLdouble* newVert = new GLdouble[3];
        ctx->combined.push_back( newVert );
    
        newVert[0] = coords[0];
        newVert[1] = coords[1];
        newVert[2] = coords[2];
        *outData = newVert;
    }
    
    template< typename Vec >
    std::vector< Vec > Triangulate
        ( 
        const vector< Vec >& aSimplePolygon
        )
    {
        std::vector< GLdouble > coords;
        for( size_t i = 0; i < aSimplePolygon.size(); ++i )
        {
            coords.push_back( aSimplePolygon[i].x() );
            coords.push_back( aSimplePolygon[i].y() );
            coords.push_back( 0 );
        }
    
        GLUtesselator* tess = gluNewTess();
        gluTessCallback( tess, GLU_TESS_BEGIN,          (void (CALLBACK *)())   tess_begin      );
        gluTessCallback( tess, GLU_TESS_EDGE_FLAG,      (void (CALLBACK *)())   tess_edgeFlag   );
        gluTessCallback( tess, GLU_TESS_VERTEX_DATA,    (void (CALLBACK *)())   tess_vertex     );
        gluTessCallback( tess, GLU_TESS_END,            (void (CALLBACK *)())   tess_end        );
        gluTessCallback( tess, GLU_TESS_COMBINE_DATA,   (void (CALLBACK *)())   tess_combine    );
        gluTessNormal( tess, 0.0, 0.0, 1.0 );
    
        TessContext ctx;
    
        gluTessBeginPolygon( tess, &ctx );
        gluTessBeginContour( tess );
    
        for( size_t i = 0; i < aSimplePolygon.size(); ++i )
        {
            gluTessVertex( tess, &coords[i*3], &coords[i*3] );
        }
    
        gluTessEndContour( tess );
        gluTessEndPolygon( tess );
    
        gluDeleteTess(tess);
    
        std::vector< Vec > ret( ctx.pts.size() );
        for( size_t i = 0; i < ret.size(); ++i )
        {
            ret[i].x() = static_cast< Vec::Scalar >( ctx.pts[i].first );
            ret[i].y() = static_cast< Vec::Scalar >( ctx.pts[i].second );
        }
    
        return ret;
    }
    

    I tend to use Eigen::Vector2f for Vec.