/* $Id: tess.c,v 1.25 2000/07/11 14:11:04 brianp Exp $ */ /* * Mesa 3-D graphics library * Version: 3.3 * Copyright (C) 1995-2000 Brian Paul * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * This file is part of the polygon tesselation code contributed by * Bogdan Sikorski */ #ifdef PC_HEADER #include "all.h" #else #include <math.h> #include <stdlib.h> #include "tess.h" #endif /* * This is ugly, but seems the easiest way to do things to make the * code work under YellowBox for Windows */ #if defined(OPENSTEP) && defined(CALLBACK) #undef CALLBACK #define CALLBACK #endif static void delete_contours(GLUtriangulatorObj *); #ifdef __CYGWIN32__ #define _CALLBACK #else #define _CALLBACK GLCALLBACK #endif static void init_callbacks(tess_callbacks * callbacks) { callbacks->begin = (void (_CALLBACK *) (GLenum)) 0; callbacks->edgeFlag = (void (_CALLBACK *) (GLboolean)) 0; callbacks->vertex = (void (_CALLBACK *) (void *)) 0; callbacks->end = (void (_CALLBACK *) (void)) 0; callbacks->error = (void (_CALLBACK *) (GLenum)) 0; } void tess_call_user_error(GLUtriangulatorObj * tobj, GLenum gluerr) { if (tobj->error == GLU_NO_ERROR) tobj->error = gluerr; if (tobj->callbacks.error != NULL) (tobj->callbacks.error) (gluerr); } GLUtriangulatorObj *GLAPIENTRY gluNewTess(void) { GLUtriangulatorObj *tobj; if ((tobj = (GLUtriangulatorObj *) malloc(sizeof(struct GLUtriangulatorObj))) == NULL) return NULL; tobj->contours = tobj->last_contour = NULL; init_callbacks(&tobj->callbacks); tobj->error = GLU_NO_ERROR; tobj->current_polygon = NULL; tobj->contour_cnt = 0; return tobj; } void GLAPIENTRY gluTessCallback(GLUtriangulatorObj * tobj, GLenum which, void (GLCALLBACK * fn) ()) { switch (which) { case GLU_BEGIN: tobj->callbacks.begin = (void (_CALLBACK *) (GLenum)) fn; break; case GLU_EDGE_FLAG: tobj->callbacks.edgeFlag = (void (_CALLBACK *) (GLboolean)) fn; break; case GLU_VERTEX: tobj->callbacks.vertex = (void (_CALLBACK *) (void *)) fn; break; case GLU_END: tobj->callbacks.end = (void (_CALLBACK *) (void)) fn; break; case GLU_ERROR: tobj->callbacks.error = (void (_CALLBACK *) (GLenum)) fn; break; default: tobj->error = GLU_INVALID_ENUM; break; } } void GLAPIENTRY gluDeleteTess(GLUtriangulatorObj * tobj) { if (tobj->error == GLU_NO_ERROR && tobj->contour_cnt) /* was gluEndPolygon called? */ tess_call_user_error(tobj, GLU_TESS_ERROR1); /* delete all internal structures */ delete_contours(tobj); free(tobj); } void GLAPIENTRY gluBeginPolygon(GLUtriangulatorObj * tobj) { /* if(tobj->error!=GLU_NO_ERROR) return; */ tobj->error = GLU_NO_ERROR; if (tobj->current_polygon != NULL) { /* gluEndPolygon was not called */ tess_call_user_error(tobj, GLU_TESS_ERROR1); /* delete all internal structures */ delete_contours(tobj); } else { if ((tobj->current_polygon = (tess_polygon *) malloc(sizeof(tess_polygon))) == NULL) { tess_call_user_error(tobj, GLU_OUT_OF_MEMORY); return; } tobj->current_polygon->vertex_cnt = 0; tobj->current_polygon->vertices = tobj->current_polygon->last_vertex = NULL; } } void GLAPIENTRY gluEndPolygon(GLUtriangulatorObj * tobj) { /*tess_contour *contour_ptr; */ /* there was an error */ if (tobj->error != GLU_NO_ERROR) goto end; /* check if gluBeginPolygon was called */ if (tobj->current_polygon == NULL) { tess_call_user_error(tobj, GLU_TESS_ERROR2); return; } tess_test_polygon(tobj); /* there was an error */ if (tobj->error != GLU_NO_ERROR) goto end; /* any real contours? */ if (tobj->contour_cnt == 0) { /* delete all internal structures */ delete_contours(tobj); return; } tess_find_contour_hierarchies(tobj); /* there was an error */ if (tobj->error != GLU_NO_ERROR) goto end; tess_handle_holes(tobj); /* there was an error */ if (tobj->error != GLU_NO_ERROR) goto end; /* if no callbacks, nothing to do */ if (tobj->callbacks.begin != NULL && tobj->callbacks.vertex != NULL && tobj->callbacks.end != NULL) { if (tobj->callbacks.edgeFlag == NULL) tess_tesselate(tobj); else tess_tesselate_with_edge_flag(tobj); } end: /* delete all internal structures */ delete_contours(tobj); } void GLAPIENTRY gluNextContour(GLUtriangulatorObj * tobj, GLenum type) { if (tobj->error != GLU_NO_ERROR) return; if (tobj->current_polygon == NULL) { tess_call_user_error(tobj, GLU_TESS_ERROR2); return; } /* first contour? */ if (tobj->current_polygon->vertex_cnt) tess_test_polygon(tobj); } void GLAPIENTRY gluTessVertex(GLUtriangulatorObj * tobj, GLdouble v[3], void *data) { tess_polygon *polygon = tobj->current_polygon; tess_vertex *last_vertex_ptr; if (tobj->error != GLU_NO_ERROR) return; if (polygon == NULL) { tess_call_user_error(tobj, GLU_TESS_ERROR2); return; } last_vertex_ptr = polygon->last_vertex; if (last_vertex_ptr == NULL) { if ((last_vertex_ptr = (tess_vertex *) malloc(sizeof(tess_vertex))) == NULL) { tess_call_user_error(tobj, GLU_OUT_OF_MEMORY); return; } polygon->vertices = last_vertex_ptr; polygon->last_vertex = last_vertex_ptr; last_vertex_ptr->data = data; last_vertex_ptr->location[0] = v[0]; last_vertex_ptr->location[1] = v[1]; last_vertex_ptr->location[2] = v[2]; last_vertex_ptr->next = NULL; last_vertex_ptr->previous = NULL; ++(polygon->vertex_cnt); } else { tess_vertex *vertex_ptr; /* same point twice? */ if (fabs(last_vertex_ptr->location[0] - v[0]) < EPSILON && fabs(last_vertex_ptr->location[1] - v[1]) < EPSILON && fabs(last_vertex_ptr->location[2] - v[2]) < EPSILON) { tess_call_user_error(tobj, GLU_TESS_ERROR6); return; } if ((vertex_ptr = (tess_vertex *) malloc(sizeof(tess_vertex))) == NULL) { tess_call_user_error(tobj, GLU_OUT_OF_MEMORY); return; } vertex_ptr->data = data; vertex_ptr->location[0] = v[0]; vertex_ptr->location[1] = v[1]; vertex_ptr->location[2] = v[2]; vertex_ptr->next = NULL; vertex_ptr->previous = last_vertex_ptr; ++(polygon->vertex_cnt); last_vertex_ptr->next = vertex_ptr; polygon->last_vertex = vertex_ptr; } } static void delete_contours(GLUtriangulatorObj * tobj) { tess_polygon *polygon = tobj->current_polygon; tess_contour *contour, *contour_tmp; tess_vertex *vertex, *vertex_tmp; /* remove current_polygon list - if exists due to detected error */ if (polygon != NULL) { if (polygon->vertices) { for (vertex = polygon->vertices; vertex != polygon->last_vertex;) { vertex_tmp = vertex->next; free(vertex); vertex = vertex_tmp; } free(vertex); } free(polygon); tobj->current_polygon = NULL; } /* remove all contour data */ for (contour = tobj->contours; contour != NULL;) { for (vertex = contour->vertices; vertex != contour->last_vertex;) { vertex_tmp = vertex->next; free(vertex); vertex = vertex_tmp; } free(vertex); contour_tmp = contour->next; free(contour); contour = contour_tmp; } tobj->contours = tobj->last_contour = NULL; tobj->contour_cnt = 0; }