基于GLUtesselator从任意多边形数据生成三角形数据

openGL从3.0以后采用了新的绘图机制,但仍不支持对任意多边形的处理,因此还是需要程序员在绘制多边形前进行网格化,并提交三角形数据。
  GLUtesselator 能将任意多边形,简化为三角形或凸多边形的组合,从而使OpenGL能绘制出任意形状的多边形。那么如何从GLUtesselator中获取三角形数据就是一个很好的问题,新的绘图机制采用VAO、VBO、EBO顶点对象方式绘图,其中原理请参考相关教材,网上也有很多讨论。我学openGL基本上都是从网上学的。
  参考网址:http://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-1-opening-a-window/
  这是中文的。https://learnopengl.com/Introduction这是英文的。
  后来我从网上找到了一份开源的,基于GLUtesselator从任意多边形数据生成三角形数据的源码,然后自己组织了VC++的工程文件,供大家学习参考。
  基本思路是定义三角形、顶点、和细分上下文三个结构,然后是一系列处理函数,最后是通过 GLUtesselator对多边形进行网格化(三角形化),但并不去绘图,只是输出三角形的坐标和三角形的顶点序号。详细内容可参考源码。
  注:工程文件是在VS2017上创建的DOS窗口程序,在自己的电脑上可能需要对opengl的支持库和包含的头文件目录进行修改。
在这里插入图片描述
附加依赖项根据而要添加。

#include "glu.h"
#include "tess.h"
#include <stdio.h>
#include <stdlib.h>

/******************************************************************************/

// 三角形结构
typedef struct Triangle {			// 三角形顶点序号?
    int v[3];
    struct Triangle *prev;			// 上一个三角形
} Triangle;

// 顶点结构
typedef struct Vertex {				// 顶点
    double pt[3];
    int index;
    struct Vertex *prev;			// 上一个顶点
} Vertex;

// 细分上下文结构
typedef struct TessContext {		// 细分上下文
    Triangle *latest_t;				// 最后三角形顶点序号数组指针?
    int n_tris;						// 三角形个数
    
    Vertex *v_prev;					// 上一个顶点指针
    Vertex *v_prevprev;				// 前两个顶点指针
    Vertex *latest_v;				// 最新顶点指针
    GLenum current_mode;			// 当前模式
    int odd_even_strip;				// 奇偶带

    void (*vertex_cb)(Vertex *, struct TessContext *);
} TessContext;

// 跳过节点
void skip_vertex(Vertex *v, TessContext *ctx);

/******************************************************************************/

// 构造细分上下文
TessContext *new_tess_context()
{
    TessContext *result = (TessContext *)malloc(sizeof (struct TessContext));
    result->latest_t = NULL;
    result->latest_v = NULL;
    result->n_tris = 0;
    result->v_prev = NULL;
    result->v_prevprev = NULL;
    result->v_prev = NULL;
    result->v_prev = NULL;
    result->vertex_cb = &skip_vertex;
    result->odd_even_strip = 0;
    return result;
}

// 释放细分上下文
void destroy_tess_context(TessContext *ctx)
{
    free(ctx);
}

// 构造顶点,分配内存
Vertex *new_vertex(TessContext *ctx, double x, double y)
{
    Vertex *result = (Vertex *)malloc(sizeof(Vertex));
    result->prev = ctx->latest_v;
    result->pt[0] = x;
    result->pt[1] = y;
    result->pt[2] = 0;

    if (ctx->latest_v == NULL) {
        result->index = 0;
    } else {
        result->index = ctx->latest_v->index+1;
    }
    return ctx->latest_v = result;
}

// 构造三角形
Triangle *new_triangle(TessContext *ctx, int v1, int v2, int v3)
{
    Triangle *result = (Triangle *)malloc(sizeof(Triangle));
    result->prev = ctx->latest_t;
    result->v[0] = v1;
    result->v[1] = v2;
    result->v[2] = v3;
    ctx->n_tris++;
    return ctx->latest_t = result;
}

/******************************************************************************/

void skip_vertex(Vertex *v, TessContext *ctx) {};

// 扇顶点(加)
void fan_vertex(Vertex *v, TessContext *ctx) {
    if (ctx->v_prevprev == NULL) {
        ctx->v_prevprev = v;
        return;
    }
    if (ctx->v_prev == NULL) {
        ctx->v_prev = v;
        return;
    }
    new_triangle(ctx, ctx->v_prevprev->index, ctx->v_prev->index, v->index);
    ctx->v_prev = v;
}

// 条带顶点(加)
void strip_vertex(Vertex *v, TessContext *ctx)
{
    if (ctx->v_prev == NULL) {
        ctx->v_prev = v;
        return;
    }
    if (ctx->v_prevprev == NULL) {
        ctx->v_prevprev = v;
        return;
    }
    if (ctx->odd_even_strip) 
	{
        new_triangle(ctx, ctx->v_prevprev->index, ctx->v_prev->index, v->index);
    }
	else 
	{
        new_triangle(ctx, ctx->v_prev->index, ctx->v_prevprev->index, v->index);
    }
    ctx->odd_even_strip = !ctx->odd_even_strip;
    ctx->v_prev = ctx->v_prevprev;
    ctx->v_prevprev = v;
}

// 三角形顶点(加)
void triangle_vertex(Vertex *v, TessContext *ctx) {
    if (ctx->v_prevprev == NULL) {
        ctx->v_prevprev = v;
        return;
    }
    if (ctx->v_prev == NULL) {
        ctx->v_prev = v;
        return;
    }
    new_triangle(ctx, ctx->v_prevprev->index, ctx->v_prev->index, v->index);
    ctx->v_prev = ctx->v_prevprev = NULL;
}

// 多边形顶点
void vertex(void *vertex_data, void *poly_data)
{
    Vertex *ptr = (Vertex *)vertex_data;
    TessContext *ctx = (TessContext *)poly_data;
    ctx->vertex_cb(ptr, ctx);
}

// 构造上下文
void begin(GLenum which, void *poly_data)
{
    TessContext *ctx = (TessContext *)poly_data; // 多边形数据构造细分上下文
    ctx->v_prev = ctx->v_prevprev = NULL;
    ctx->odd_even_strip = 0;
    switch (which) 
	{
    case GL_TRIANGLES: 
		ctx->vertex_cb = &triangle_vertex; 
		break;
    case GL_TRIANGLE_STRIP: 
		ctx->vertex_cb = &strip_vertex; 
		break;
    case GL_TRIANGLE_FAN: 
		ctx->vertex_cb = &fan_vertex; 
		break;
    default:
        fprintf(stderr, "ERROR, can't handle %d\n", (int)which);
        ctx->vertex_cb = &skip_vertex;
    }
}

// 合并
void combine(const GLdouble newVertex[3],			// 新顶点						
             const void *neighborVertex[4],			// 邻顶点
             const GLfloat neighborWeight[4],		//
			 void **outData,						// 输出数据
			 void *polyData)						// 多边形数据
{
    TessContext *ctx = (TessContext *)polyData;
    Vertex *result = new_vertex(ctx, newVertex[0], newVertex[1]);
    *outData = result;
}

// 输出
void write_output(TessContext *ctx,					// 输入
					double **coordinates_out,		// 输出
					int **tris_out,					// 输出
					int *vc,						// 输出
					int *tc)						// 输出
{
    int n_verts = 1 + ctx->latest_v->index;
    *vc = n_verts;
    int n_tris_copy = ctx->n_tris;
    *tc = ctx->n_tris;
    *coordinates_out = malloc(n_verts * sizeof(double) * 2);
    *tris_out = (ctx->n_tris ? malloc(ctx->n_tris * sizeof(int) * 3) : NULL);

    while (ctx->latest_v) 
	{
        (*coordinates_out)[2*ctx->latest_v->index]   = ctx->latest_v->pt[0];
        (*coordinates_out)[2*ctx->latest_v->index+1] = ctx->latest_v->pt[1];
        Vertex *prev = ctx->latest_v->prev;
        free(ctx->latest_v);
        ctx->latest_v = prev;
    }

    while (ctx->latest_t) 
	{
        (*tris_out)[3*(n_tris_copy-1)]   = ctx->latest_t->v[0];
        (*tris_out)[3*(n_tris_copy-1)+1] = ctx->latest_t->v[1];
        (*tris_out)[3*(n_tris_copy-1)+2] = ctx->latest_t->v[2];
        Triangle *prev = ctx->latest_t->prev;
        free(ctx->latest_t);
        ctx->latest_t = prev;
        n_tris_copy--;
    }
}

// 多边形细分
void tessellate
    (double **verts,
     int *nverts,
     int **tris,
     int *ntris,
     const double **contoursbegin, 
     const double **contoursend)
{
    const double *contourbegin, *contourend;
    Vertex *current_vertex;
    GLUtesselator *tess;
    TessContext *ctx;

    tess = gluNewTess();
    ctx = new_tess_context();
    gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
    gluTessCallback(tess, GLU_TESS_VERTEX_DATA,  (GLvoid (*) ()) &vertex);
    gluTessCallback(tess, GLU_TESS_BEGIN_DATA,   (GLvoid (*) ()) &begin);
    gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (GLvoid (*) ()) &combine);

    gluTessBeginPolygon(tess, ctx);
    do {
        contourbegin = *contoursbegin++;
        contourend = *contoursbegin;
        gluTessBeginContour(tess);
        while (contourbegin != contourend) {
            current_vertex = new_vertex(ctx, contourbegin[0], contourbegin[1]);
            contourbegin += 2;
            gluTessVertex(tess, current_vertex->pt, current_vertex);
        }
        gluTessEndContour(tess);
    } while (contoursbegin != (contoursend - 1));
    gluTessEndPolygon(tess);

    write_output(ctx, verts, tris, nverts, ntris);
    destroy_tess_context(ctx);
    gluDeleteTess(tess);
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值