glutesselator 总是在 gluTessEndPolygon() 处崩溃

glutesselator always crashes at gluTessEndPolygon()

我使用 glu 曲面细分来细分复杂的多边形。下面列出了简化的代码。

它总是在 gluTessEndPolygon(GLUtessobj) 崩溃并出现错误:

错误:0xC0000005:访问冲突读取位置 0x57783b39;

当多边形的点数较少(<100)时,代码有效。

我就是想不通为什么。

typedef boost::geometry::model::point<float, 2, boost::geometry::cs::cartesian> pt;
typedef boost::geometry::model::polygon<pt> Polygon;
typedef boost::geometry::model::ring<pt> Ring;
vector<Polygon> g_myPolys;


// ------Static variables used in glu tessellation------
static GLUtesselator  *GLUtessobj;
static unsigned int   s_gltri_type;
static int s_tess_orient;
static int s_cur_pt_idx;
//    Create an array to hold pointers to allocated vertices created by "combine" callback,
//    so that they may be deleted after tessellation.
static std::vector<GLdouble*>     s_combineVertexArray;

// Store tessellated results
static std::vector<double> s_vecTriVerts;  // Store area objects' tessellated triangle( triangle fan, triangle strip and triangles) vertices.
static std::vector<int> s_vecTriStripVertCnts;   // Store every triangle strips' start indices in m_vecTriVerts.
static std::vector<int> s_vecTriStripFirstIdx;   // Store every triangle strips' vertex count start from its start index.

static std::vector<int> s_vecTriFanVertCnts;  // Store every triangle fans' start indices in m_vecTriVerts.
static std::vector<int> s_vecTriFanFirstIdx;  // Store every triangle fans' vertex count start from its start index.

static std::vector<int> s_vecTrisVertCnts;   // Store every triangles' start indices in m_vecTriVerts.
static std::vector<int> s_vecTrisFirstIdx;   // Store every triangles' vertex count start from its start index.

static int s_cur_tri_fans_vert_cnt;
static int s_cur_tri_strips_vert_cnt;
static int s_cur_tris_vert_cnt;

static std::vector<double*> s_vecTmp;

void beginCallback(GLenum which)
{
    s_gltri_type = which;
    switch ( s_gltri_type)
    {
    case GL_TRIANGLE_FAN:
        s_vecTriFanFirstIdx.push_back(s_cur_pt_idx);
        s_cur_tri_fans_vert_cnt = 0;
        break;
    case GL_TRIANGLE_STRIP:
        s_vecTriStripFirstIdx.push_back(s_cur_pt_idx);
        s_cur_tri_strips_vert_cnt = 0;
        break;
    case GL_TRIANGLES:
        s_vecTrisFirstIdx.push_back(s_cur_pt_idx);
        s_cur_tris_vert_cnt = 0;
        break;
    }
}


void vertexCallback(GLvoid *vertex)
{
    GLdouble *pv = (GLdouble *) vertex;
    s_vecTriVerts.push_back(pv[0]);
    s_vecTriVerts.push_back(pv[1]);

    s_cur_pt_idx ++;

    switch ( s_gltri_type)
    {
    case GL_TRIANGLE_FAN:
        s_cur_tri_fans_vert_cnt ++;
        break;
    case GL_TRIANGLE_STRIP:
        s_cur_tri_strips_vert_cnt ++;
        break;
    case GL_TRIANGLES:
        s_cur_tris_vert_cnt ++;
        break;
    }
}

void combineCallback(GLdouble coords[3],
    GLdouble *vertex_data[4],
    GLfloat weight[4], GLdouble **dataOut )
{
    GLdouble *vertex = (GLdouble *)malloc(6 * sizeof(GLdouble));

    vertex[0] = coords[0];
    vertex[1] = coords[1];
    vertex[2] = coords[2];
    vertex[3] = vertex[4] = vertex[5] = 0.0;

    *dataOut = vertex;

    s_combineVertexArray.push_back(vertex);
}

void endCallback()
{
    switch ( s_gltri_type)
    {
    case GL_TRIANGLE_FAN:
        s_vecTriFanVertCnts.push_back(s_cur_tri_fans_vert_cnt);
        break;
    case GL_TRIANGLE_STRIP:
        s_vecTriStripVertCnts.push_back(s_cur_tri_strips_vert_cnt);
        break;
    case GL_TRIANGLES:
        s_vecTrisVertCnts.push_back(s_cur_tris_vert_cnt);
        break;
    }
}

void errorCallback(GLenum errorCode)
{
    const GLubyte *estring;
    estring = gluErrorString(errorCode);
    printf ("Tessellation Error: %s\n", estring);
}

void Tessellate()
{
    // Create tessellate object
    GLUtessobj = gluNewTess();

    //  Register the callbacks
    gluTessCallback(GLUtessobj, GLU_TESS_BEGIN,   (void (__stdcall*)())&beginCallback);
    gluTessCallback(GLUtessobj, GLU_TESS_VERTEX,  (void (__stdcall*)())&vertexCallback);
    gluTessCallback(GLUtessobj, GLU_TESS_END,     (void (__stdcall*)())&endCallback);
    gluTessCallback(GLUtessobj, GLU_TESS_COMBINE, (void (__stdcall*)())&combineCallback);
    gluTessCallback(GLUtessobj, GLU_TESS_ERROR,   (void (__stdcall*)())&errorCallback);

    gluTessProperty(GLUtessobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );

    gluTessBeginPolygon(GLUtessobj, NULL);
    gluTessBeginContour(GLUtessobj);

    Polygon pp =  g_myPolys[0];
    for ( int i = 0; i < pp.outer().size(); i ++)
    {
        GLdouble  *p = new GLdouble[3];
        s_vecTmp.push_back(p);

        p[0] = pp.outer()[i].get<0>();
        p[1] = pp.outer()[i].get<1>();
        p[2] = 0.0;

        gluTessVertex( GLUtessobj, p, p ) ;
    }

    gluTessEndContour(GLUtessobj);
    gluTessEndPolygon(GLUtessobj);

    gluDeleteTess(GLUtessobj);

    for ( int i = 0; i < s_vecTmp.size(); i ++)
        delete[] s_vecTmp[i];
    s_vecTmp.clear();

    // Free up any "Combine" vertices created
    for(unsigned int i = 0; i < s_combineVertexArray.size(); i++)
        free (s_combineVertexArray[i]);
    s_combineVertexArray.clear();
}

有一件事让我立刻感到奇怪,你在那里对 __stdcall 进行了转换。

gluTessCallback(GLUtessobj, GLU_TESS_BEGIN,   (void (__stdcall*)())&beginCallback);

你为什么要这么做?如果您的编译器抱怨调用约定不兼容,那么您应该做的最后一件事就是转换调用约定。如果你施行召唤约定,等待你的只有绝望和恐惧。强制转换指针已经是个坏主意(在 C++ 中强制转换 from/to void* 还可以,但仅此而已)。

然后您还可以用指针做一些其他奇怪的事情。例如,您将 std::vector 与手动管理的内存 (new GLdouble[3]) 混合使用。说真的,为什么?!

我强烈建议您简化数据结构并清理指针杂耍。很可能您在代码中的某处写入了一些越界缓冲区,但很难看出确切位置。