Files
DoomRTX/neo/engine/opengl/tess/tessellate.c
T
Justin Marshall 8c4a087aa9 Filesystem update.
2026-05-09 07:54:14 -07:00

232 lines
6.3 KiB
C

#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 = (double *)malloc(n_verts * sizeof(double) * 2);
*tris_out = (int *)(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);
}