Merge commit 'origin/master' into gallium-0.2
[mesa.git] / src / glu / mesa / tess.c
index 919643cb8e20b1bb9959167a8e710bbbdf05a7be..341d29bae3a8e8067cc3da1ece656fe9b33b20e1 100644 (file)
-/* $Id: tess.c,v 1.24 2000/02/10 17:45:52 brianp Exp $ */
 
 /*
  * Mesa 3-D graphics library
  * Version:  3.3
+ * Copyright (C) 1995-2000  Brian Paul
  *
- * Copyright (C) 1999-2000  Brian Paul   All Rights Reserved.
+ * 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.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
+ * 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.
  *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * 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.
  */
 
-/*****************************************************************************
- *
- * GLU 1.3 Polygon Tessellation by Gareth Hughes <garethh@bell-labs.com>
- *
- *****************************************************************************/
 
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
+/*
+ * This file is part of the polygon tesselation code contributed by
+ * Bogdan Sikorski
+ */
 
-#include "gluP.h"
 
+#ifdef PC_HEADER
+#include "all.h"
+#else
+#include <math.h>
+#include <stdlib.h>
 #include "tess.h"
-#include "tess_macros.h"
-#include "tess_fist.h"
-#if 0
-#include "tess_grid.h"
-#endif
-
-
-#define TESS_CHECK_ERRORS(t)   if ( (t)->error != GLU_NO_ERROR ) goto cleanup
-
-#ifdef DEBUG
-GLint          tess_dbg_level;
 #endif
 
 
-/*****************************************************************************
- * tess_error_callback
- *
- * Internal error handler.  Call the user-registered error callback.
- *
- * 2nd arg changed from 'errno' to 'errnum' since MSVC defines errnum as
- *  a macro (of all things) and thus breaks the build -tjump
- *****************************************************************************/
-void tess_error_callback( GLUtesselator *tobj, GLenum errnum )
-{
-    if ( tobj->error == GLU_NO_ERROR )
-    {
-       tobj->error = errnum;
-    }
-
-    if ( tobj->callbacks.errorData != NULL )
-    {
-       ( tobj->callbacks.errorData )( errnum, tobj->data );
-    }
-    else if ( tobj->callbacks.error != NULL )
-    {
-       ( tobj->callbacks.error )( errnum );
-    }
-}
-
-
-/*****************************************************************************
- * init_callbacks
- *****************************************************************************/
-static void init_callbacks( tess_callbacks_t *callbacks )
-{
-    callbacks->begin        = ( void (GLCALLBACKPCAST)(GLenum) ) NULL;
-    callbacks->beginData    = ( void (GLCALLBACKPCAST)(GLenum, void *) ) NULL;
-    callbacks->edgeFlag     = ( void (GLCALLBACKPCAST)(GLboolean) ) NULL;
-    callbacks->edgeFlagData = ( void (GLCALLBACKPCAST)(GLboolean, void *) ) NULL;
-    callbacks->vertex       = ( void (GLCALLBACKPCAST)(void *) ) NULL;
-    callbacks->vertexData   = ( void (GLCALLBACKPCAST)(void *, void *) ) NULL;
-    callbacks->end          = ( void (GLCALLBACKPCAST)(void) ) NULL;
-    callbacks->endData      = ( void (GLCALLBACKPCAST)(void *) ) NULL;
-    callbacks->error        = ( void (GLCALLBACKPCAST)(GLenum) ) NULL;
-    callbacks->errorData    = ( void (GLCALLBACKPCAST)(GLenum, void *) ) NULL;
-    callbacks->combine      = ( void (GLCALLBACKPCAST)(GLdouble [3], void *[4],
-                                                  GLfloat [4], void **) ) NULL;
-    callbacks->combineData  = ( void (GLCALLBACKPCAST)(GLdouble [3], void *[4],
-                                                  GLfloat [4], void **,
-                                                  void *) ) NULL;
-}
-
-
-/*****************************************************************************
- * find_normal
- *****************************************************************************/
-static GLenum find_normal( GLUtesselator *tobj )
-{
-    tess_contour_t     *contour = tobj->current_contour;
-    tess_vertex_t      *va, *vb, *vc;
-    GLdouble           a[3], b[3], c[3];
-
-    MSG( 15, "      --> find_normal( tobj:%p )\n", tobj );
-
-    if ( contour == NULL ) { return GLU_ERROR; }
-
-    va = contour->vertices;
-    vb = va->next;
-
-    /* If va and vb are the same point, keep looking for a different vertex. */
-
-    while ( IS_EQUAL_3DV( va->coords, vb->coords ) && ( vb != va ) ) {
-       vb = vb->next;
-    }
-
-    if ( vb == va ) {
-       /* FIXME: What error is this? */
-       tess_error_callback( tobj, GLU_TESS_ERROR7 );
-    }
-
-    SUB_3V( a, vb->coords, va->coords );
-
-    for ( vc = vb->next; vc != va; vc = vc->next )
-    {
-       SUB_3V( b, vc->coords, va->coords );
-
-       CROSS_3V( c, a, b );
-
-       if ( ! IS_ZERO_3DV( c ) )
-       {
-           MSG( 15, "            using (%.2f,%.2f) -> (%.2f,%.2f) -> (%.2f,%.2f)\n",
-                va->coords[X], va->coords[Y],
-                vb->coords[X], vb->coords[Y],
-                vc->coords[X], vc->coords[Y] );
-
-           COPY_3V( contour->plane.normal, c );
-           NORMALIZE_3DV( contour->plane.normal );
-
-           contour->plane.dist = - DOT_3V( contour->plane.normal,
-                                           va->coords );
-
-           MSG( 15, "      <-- find_normal( tobj:%p ) n: (%.2f, %.2f, %.2f)\n", tobj, contour->plane.normal[X], contour->plane.normal[Y], contour->plane.normal[Z] );
-           return GLU_NO_ERROR;
-       }
-    }
-    /* FIXME: What error is this? */
-    tess_error_callback( tobj, GLU_TESS_ERROR7 );
-
-    return GLU_ERROR;
-}
-
-/*****************************************************************************
- * twice_contour_area
- *
- * Calculate the twice the signed area of the given contour.  Used to
- * determine the contour's orientation amongst other things.
- *****************************************************************************/
-GLdouble twice_contour_area( tess_contour_t *contour )
-{
-    tess_vertex_t      *vertex = contour->vertices;
-    GLdouble           area, x, y;
-
-    area = 0.0;
-
-    x = vertex->v[X];
-    y = vertex->v[Y];
-
-    vertex = vertex->next;
-
-    do
-    {
-       area += ( (vertex->v[X] - x) * (vertex->next->v[Y] - y) -
-                 (vertex->v[Y] - y) * (vertex->next->v[X] - x) );
-       vertex = vertex->next;
-    }
-    while ( vertex != contour->vertices );
-
-    return area;
-}
-
-/*****************************************************************************
- * project_current_contour
- *
- * Project the contour's vertices onto the tessellation plane.  We perform
- * a complex rotation here to allow non-axis-aligned tessellation normals.
- *****************************************************************************/
-static void project_current_contour( GLUtesselator *tobj )
-{
-    tess_contour_t     *current = tobj->current_contour;
-    tess_vertex_t      *vertex;
-    GLdouble           zaxis[3] = { 0.0, 0.0, 1.0 }, znormal[3], xnormal[3];
-    GLdouble           dot, rotx, roty;
-    GLint              i;
-
-    MSG( 15, "      --> project_current_contour( tobj:%p )\n", tobj );
-
-    if ( current == NULL ) { return; }
-
-    /* Rotate the plane normal around the y-axis. */
-
-    znormal[X] = current->plane.normal[X];
-    znormal[Y] = 0.0;
-    znormal[Z] = current->plane.normal[Z];
-
-    dot = DOT_3V( znormal, zaxis );
-    current->roty = roty = acos( dot );
-
-    /* Rotate the plane normal around the x-axis. */
-
-    xnormal[X] = cos( roty ) * znormal[X] - sin( roty ) * znormal[Z];
-    xnormal[Y] = znormal[Y];
-    xnormal[Z] = sin( roty ) * znormal[X] + cos( roty ) * znormal[Z];
-
-    dot = DOT_3V( xnormal, zaxis );
-    current->rotx = rotx = acos( dot );
-
-    for ( vertex = current->vertices, i = 0;
-         i < current->num_vertices; vertex = vertex->next, i++ )
-    {
-       tess_plane_t    *plane = &current->plane;
-       GLdouble        proj[3], yrot[3], xrot[3];
-
-       /* FIXME: This needs a cleanup, 'cos I'm sure it's inefficient. */
-
-       proj[X] = vertex->coords[X] - plane->dist * plane->normal[X];
-       proj[Y] = vertex->coords[Y] - plane->dist * plane->normal[Y];
-       proj[Z] = vertex->coords[Z] - plane->dist * plane->normal[Z];
-
-       yrot[X] = cos( roty ) * proj[X] - sin( roty ) * proj[Z];
-       yrot[Y] = proj[Y];
-       yrot[Z] = sin( roty ) * proj[X] + cos( roty ) * proj[Z];
-
-       xrot[X] = yrot[X];
-       xrot[Y] = cos( rotx ) * yrot[Y] - sin( rotx ) * yrot[Z];
-       xrot[Z] = sin( rotx ) * yrot[Y] + cos( rotx ) * yrot[Z];
-
-       vertex->v[X] = xrot[X];
-       vertex->v[Y] = xrot[Y];
-
-       ACC_BBOX_2V( vertex->v, tobj->mins, tobj->maxs );
-       ACC_BBOX_2V( vertex->v, current->mins, current->maxs );
-    }
-
-    current->area = twice_contour_area( current );
-    current->orientation = ( current->area >= 0.0 ) ? GLU_CCW : GLU_CW;
-
-    MSG( 15, "            area: %.2f orientation: %s\n",
-        current->area, ( current->orientation == GLU_CCW ) ? "CCW" : "CW" );
-
-    MSG( 15, "      <-- project_current_contour( tobj:%p )\n", tobj );
-}
-
-/*****************************************************************************
- * save_current_contour
- *****************************************************************************/
-static GLenum save_current_contour( GLUtesselator *tobj )
-{
-    tess_contour_t     *current = tobj->current_contour;
-    tess_vertex_t      *vertex;
-    GLint              i;
-
-    if ( current == NULL ) { return GLU_ERROR; }
-
-    if ( tobj->contours == NULL )
-    {
-       tobj->contours = tobj->last_contour = current;
-       current->next = current->prev = NULL;
-
-       tobj->orientation = current->orientation;
-    }
-    else
-    {
-       current->prev = tobj->last_contour;
-
-       tobj->last_contour->next = current;
-       tobj->last_contour = current;
-
-       current->next = NULL;
-    }
-
-    for ( vertex = current->vertices, i = 0;
-         i < current->num_vertices; vertex = vertex->next, i++ )
-    {
-       vertex->edge_flag = GL_TRUE;
-    }
-
-    current->type = GLU_UNKNOWN;
-
-    tobj->num_contours++;
-    tobj->current_contour = NULL;
-
-    return GLU_NO_ERROR;
-}
-
-/*****************************************************************************
- * inspect_current_contour
- *****************************************************************************/
-static void inspect_current_contour( GLUtesselator *tobj )
-{
-    tess_contour_t     *current = tobj->current_contour;
-    GLdouble           origin[3] = { 0.0, 0.0, 0.0 };
-    GLboolean          calc_normal = GL_FALSE;
-
-    MSG( 15, "    --> inspect_current_contour( tobj:%p )\n", tobj );
-
-    if ( current->num_vertices < 3 )
-    {
-       MSG( 15, "          count %d < 3, deleting\n", current->num_vertices );
-       delete_contour( &tobj->current_contour );
-       return;
-    }
-
-    current->last_vertex->next = current->vertices;
-    current->vertices->prev = current->last_vertex;
-
-    MSG( 15, "          current normal: (%.2f, %.2f, %.2f)\n", current->plane.normal[X], current->plane.normal[Y], current->plane.normal[Z] );
-
-    if ( IS_EQUAL_3DV( current->plane.normal, origin ) )
-    {
-       /* We haven't been given a normal, so let's take a guess. */
-       if ( find_normal( tobj ) == GLU_ERROR ) {
-           return;
-       }
-
-       COPY_3V( tobj->plane.normal, current->plane.normal );
-       tobj->plane.dist = current->plane.dist;
-
-       calc_normal = GL_TRUE;
-    }
-
-    project_current_contour( tobj );
-
-    if ( calc_normal && ( tobj->current_contour->orientation == GLU_CW ) )
-    {
-       MSG( 15, "          oops, let's try that again...\n" );
-
-       /*
-        * FIXME: We've used a reflex angle to calculate the normal.  At
-        * the moment, we simply reverse the normal and re-project the
-        * contour, but this is sloooow...
-        */
-       NEG_3V( tobj->plane.normal );
-       NEG_3V( tobj->current_contour->plane.normal );
-
-       project_current_contour( tobj );
-    }
-
-    if ( save_current_contour( tobj ) == GLU_ERROR ) {
-       return;
-    }
-
-    MSG( 15, "    <-- inspect_current_contour( tobj:%p )\n", tobj );
-}
-
-/*****************************************************************************
- * reverse_contour
- *****************************************************************************/
-void reverse_contour( tess_contour_t *contour )
-{
-    tess_vertex_t      *current = contour->vertices;
-    GLint              i;
-
-    for ( i = 0 ; i < contour->num_vertices ; i++ )
-    {
-       tess_vertex_t   *next = current->next;
-       tess_vertex_t   *prev = current->prev;
-
-       current->next = prev;
-       current->prev = next;
-
-       current = next;
-    }
-
-    contour->orientation =
-       ( contour->orientation == GLU_CCW ) ? GLU_CW : GLU_CCW;
-
-    contour->last_vertex = contour->vertices->prev;
-}
-
-/*****************************************************************************
- * orient_contours
- *
- * Sum the signed areas of the contours, and orient the contours such that
- * this sum is nonnegative.
- *****************************************************************************/
-static void orient_contours( GLUtesselator *tobj )
-{
-    tess_contour_t     *contour = tobj->contours;
-    GLdouble           sum = 0.0;
-    GLint              i;
-
-    MSG( 15, "    --> orient_contours( tobj:%p )\n", tobj );
-
-    /* Sum the signed areas of all contours */
-    for ( i = 0 ; i < tobj->num_contours ; i++ )
-    {
-       sum += contour->area;
-       contour = contour->next;
-    }
-
-    MSG( 15, "          signed area: %.2f\n", sum );
-
-    if ( sum < -GLU_TESS_EPSILON )
-    {
-       for ( i = 0 ; i < tobj->num_contours ; i++ )
-       {
-           contour->area = ABSD( contour->area );
-           reverse_contour( contour );
-
-           contour = contour->next;
-       }
-    }
-    else
-    {
-       for ( i = 0 ; i < tobj->num_contours ; i++ )
-       {
-           contour->area = ABSD( contour->area );
-
-           contour = contour->next;
-       }
-    }
-
-    tobj->orientation = tobj->contours->orientation;
-
-    MSG( 15, "    <-- orient_contours( tobj:%p ) orient: %s\n",
-        tobj, ( tobj->orientation == GLU_CCW ) ? "GLU_CCW" : "GLU_CW" );
-}
-
-
-/*****************************************************************************
- * delete_contour
- *
- * Delete the given contour and set the pointer to NULL.
- *****************************************************************************/
-void delete_contour( tess_contour_t **contour )
-{
-    tess_vertex_t      *vertex, *next;
-    GLint              i;
-
-    if ( *contour == NULL ) { return; }
-
-    vertex = (*contour)->vertices;
-
-    for ( i = 0 ; i < (*contour)->num_vertices ; i++ )
-    {
-       next = vertex->next;
-       free( vertex );
-       vertex = next;
-    }
-
-    free( *contour );
-    *contour = NULL;
-}
-
-/*****************************************************************************
- * delete_all_contours
- *****************************************************************************/
-static void delete_all_contours( GLUtesselator *tobj )
-{
-    tess_contour_t     *current, *next_contour;
-    GLint              i;
-
-    if ( tobj->current_contour != NULL ) {
-       delete_contour( &tobj->current_contour );
-    }
-
-    for ( current = tobj->contours, i = 0 ; i < tobj->num_contours ; i++ )
-    {
-       tess_vertex_t   *vertex = current->vertices, *next_vertex;
-       GLint           j;
-
-       for ( j = 0 ; j < current->num_vertices ; j ++ )
-       {
-           next_vertex = vertex->next;
-           free( vertex );
-           vertex = next_vertex;
-       }
-       next_contour = current->next;
-
-       free( current );
-       current = next_contour;
-    }
-
-    tobj->num_contours = tobj->num_vertices = 0;
-    tobj->contours = tobj->last_contour = NULL;
-
-    CLEAR_BBOX_2DV( tobj->mins, tobj->maxs );
-}
-
-
-/*****************************************************************************
- * tess_cleanup
- *****************************************************************************/
-static void tess_cleanup( GLUtesselator *tobj )
-{
-    MSG( 15, "  -> tess_cleanup( tobj:%p )\n", tobj );
-
-    if ( tobj->current_contour != NULL ) {
-       delete_contour( &tobj->current_contour );
-    }
-    if ( tobj->contours != NULL ) {
-       delete_all_contours( tobj );
-    }
-
-    MSG( 15, "  <- tess_cleanup( tobj:%p )\n", tobj );
-}
-
-
-/*****************************************************************************
- * tess_msg
- *****************************************************************************/
-INLINE void tess_msg( GLint level, char *format, ... )
-{
-#ifdef DEBUG
-    va_list ap;
-    va_start( ap, format );
-
-    if ( level <= tess_dbg_level ) {
-       vfprintf( DBG_STREAM, format, ap );
-       fflush( DBG_STREAM );
-    }
-
-    va_end( ap );
-#endif
-}
-
-INLINE void tess_info( char *file, GLint line )
-{
-#ifdef DEBUG
-    fprintf( DBG_STREAM, "%9.9s:%d:\t ", file, line );
-#endif
-}
-
-
-
-/*****************************************************************************
- *
- *                     GLU TESSELLATION FUNCTIONS
- *
- *****************************************************************************/
-
-
-/*****************************************************************************
- * gluNewTess
- *****************************************************************************/
-GLUtesselator* GLAPIENTRY gluNewTess( void )
-{
-    GLUtesselator *tobj;
-
-#ifdef DEBUG
-    if ( getenv( "MESA_TESS_DBG_LEVEL" ) ) {
-       tess_dbg_level = atoi( getenv( "MESA_TESS_DBG_LEVEL" ) );
-    } else {
-       tess_dbg_level = DBG_LEVEL_BASE;
-    }
+/*
+ * 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
 
-    MSG( 15, "-> gluNewTess()\n" );
-
-    tobj = malloc( sizeof(GLUtesselator) );
-    if ( tobj == NULL ) {
-       return NULL;
-    }
-
-    init_callbacks( &tobj->callbacks );
 
-    tobj->winding_rule = GLU_TESS_WINDING_ODD;
-    tobj->boundary_only = GL_FALSE;
-    tobj->tolerance = GLU_TESS_EPSILON;
-    tobj->orientation = GLU_UNKNOWN;
+static void delete_contours(GLUtriangulatorObj *);
 
-    tobj->data = NULL;
-
-    tobj->num_contours = 0;
-    tobj->contours = tobj->last_contour = NULL;
-    tobj->current_contour = NULL;
-
-    CLEAR_BBOX_2DV( tobj->mins, tobj->maxs );
-
-    tobj->num_vertices = 0;
-    tobj->sorted_vertices = NULL;
-#if 0
-    tobj->grid = NULL;
+#ifdef __CYGWIN32__
+#define _CALLBACK
+#else
+#define _CALLBACK GLCALLBACK
 #endif
-    tobj->edge_flag = GL_FALSE;
-    tobj->label = 0;
-
-    ZERO_3V( tobj->plane.normal );
-    tobj->plane.dist = 0.0;
 
-    tobj->error = GLU_NO_ERROR;
 
-    MSG( 15, "<- gluNewTess() tobj:%p\n", tobj );
-    return tobj;
-}
-
-
-/*****************************************************************************
- * gluDeleteTess
- *****************************************************************************/
-void GLAPIENTRY gluDeleteTess( GLUtesselator *tobj )
+static void
+init_callbacks(tess_callbacks * callbacks)
 {
-    MSG( 15, "-> gluDeleteTess( tobj:%p )\n", tobj );
-
-    if ( ( tobj->error == GLU_NO_ERROR ) && ( tobj->num_contours > 0 ) )
-    {
-       /* gluEndPolygon was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR3 );
-    }
-
-    /* Delete all internal structures. */
-    tess_cleanup( tobj );
-    free( tobj );
-
-    MSG( 15, "<- gluDeleteTess()\n" );
+   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;
 }
 
-
-/*****************************************************************************
- * gluTessBeginPolygon
- *****************************************************************************/
-void GLAPIENTRY gluTessBeginPolygon( GLUtesselator *tobj, void *polygon_data )
+void
+tess_call_user_error(GLUtriangulatorObj * tobj, GLenum gluerr)
 {
-    MSG( 15, "-> gluTessBeginPolygon( tobj:%p data:%p )\n", tobj, polygon_data );
-
-    tobj->error = GLU_NO_ERROR;
-
-    if ( tobj->current_contour != NULL )
-    {
-       /* gluEndPolygon was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR3 );
-       tess_cleanup( tobj );
-    }
-
-    tobj->data = polygon_data;
-    tobj->num_vertices = 0;
-    tobj->edge_flag = GL_FALSE;
-    tobj->label = 0;
-
-    MSG( 15, "<- gluTessBeginPolygon( tobj:%p data:%p )\n", tobj, polygon_data );
+   if (tobj->error == GLU_NO_ERROR)
+      tobj->error = gluerr;
+   if (tobj->callbacks.error != NULL)
+      (tobj->callbacks.error) (gluerr);
 }
 
-
-/*****************************************************************************
- * gluTessBeginContour
- *****************************************************************************/
-void GLAPIENTRY gluTessBeginContour( GLUtesselator *tobj )
+GLUtriangulatorObj *GLAPIENTRY
+gluNewTess(void)
 {
-    MSG( 15, "  -> gluTessBeginContour( tobj:%p )\n", tobj );
-    TESS_CHECK_ERRORS( tobj );
-
-    if ( tobj->current_contour != NULL )
-    {
-       /* gluTessEndContour was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR4 );
-       return;
-    }
-
-    tobj->current_contour = malloc( sizeof(tess_contour_t) );
-    if ( tobj->current_contour == NULL ) {
-       tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
-       return;
-    }
-
-    COPY_3V( tobj->current_contour->plane.normal, tobj->plane.normal );
-    tobj->current_contour->plane.dist = tobj->plane.dist;
-
-    tobj->current_contour->area = 0.0;
-    tobj->current_contour->orientation = GLU_UNKNOWN;
-
-    tobj->current_contour->label = 0;
-    tobj->current_contour->winding = 0;
-
-    /*tobj->current_contour->rotx = tobj->current_contour->roty = 0.0;*/
-
-    CLEAR_BBOX_2DV( tobj->current_contour->mins,
-                   tobj->current_contour->maxs );
-
-    tobj->current_contour->num_vertices = 0;
-    tobj->current_contour->vertices =
-       tobj->current_contour->last_vertex = NULL;
-
-    tobj->current_contour->reflex_vertices = NULL;
-
- cleanup:
-    MSG( 15, "  <- gluTessBeginContour( tobj:%p )\n", tobj );
-    return;
+   GLUtriangulatorObj *tobj;
+
+   if ((tobj = (GLUtriangulatorObj *)
+       malloc(sizeof(struct GLUtesselator))) == 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;
 }
 
 
-/*****************************************************************************
- * gluTessVertex
- *****************************************************************************/
-void GLAPIENTRY gluTessVertex( GLUtesselator *tobj, GLdouble coords[3],
-                              void *vertex_data )
+void GLAPIENTRY
+gluTessCallback(GLUtriangulatorObj * tobj, GLenum which,
+               void (GLCALLBACK * fn) ())
 {
-    tess_contour_t             *current = tobj->current_contour;
-    tess_vertex_t              *last_vertex;
-
-    MSG( 15, "    -> gluTessVertex( tobj:%p coords:(%.2f,%.2f,%.2f) )\n", tobj, coords[0], coords[1], coords[2] );
-    TESS_CHECK_ERRORS( tobj );
-
-    if ( current == NULL )
-    {
-       /* gluTessBeginContour was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR2 );
-       return;
-    }
-
-    tobj->num_vertices++;
-
-    last_vertex = current->last_vertex;
-
-    if ( last_vertex == NULL )
-    {
-       last_vertex = malloc( sizeof(tess_vertex_t) );
-       if ( last_vertex == NULL ) {
-           tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
-           return;
-       }
-
-       current->vertices = last_vertex;
-       current->last_vertex = last_vertex;
-
-       last_vertex->index = -1;
-       last_vertex->data = vertex_data;
-
-       last_vertex->coords[X] = coords[X];
-       last_vertex->coords[Y] = coords[Y];
-       last_vertex->coords[Z] = coords[Z];
-
-       last_vertex->v[X] = 0.0;
-       last_vertex->v[Y] = 0.0;
-
-       last_vertex->edge_flag = GL_TRUE;
-
-       last_vertex->side = 0.0;
-
-       last_vertex->next = NULL;
-       last_vertex->prev = NULL;
-
-       current->num_vertices++;
-    }
-    else
-    {
-       tess_vertex_t   *vertex;
-
-       vertex = malloc( sizeof(tess_vertex_t) );
-       if ( vertex == NULL ) {
-           tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
-           return;
-       }
-
-       vertex->index = -1;
-       vertex->data = vertex_data;
-
-       vertex->coords[X] = coords[X];
-       vertex->coords[Y] = coords[Y];
-       vertex->coords[Z] = coords[Z];
-
-       vertex->v[X] = 0.0;
-       vertex->v[Y] = 0.0;
-
-       vertex->edge_flag = GL_TRUE;
-
-       vertex->side = 0.0;
-
-       vertex->next = NULL;
-       vertex->prev = last_vertex;
-
-       current->num_vertices++;
-
-       last_vertex->next = vertex;
-       current->last_vertex = vertex;
-    }
-
- cleanup:
-    MSG( 15, "    <- gluTessVertex( tobj:%p )\n", tobj );
-    return;
+   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;
+   }
 }
 
 
-/*****************************************************************************
- * gluTessEndContour
- *****************************************************************************/
-void GLAPIENTRY gluTessEndContour( GLUtesselator *tobj )
-{
-    MSG( 15, "  -> gluTessEndContour( tobj:%p )\n", tobj );
-    TESS_CHECK_ERRORS( tobj );
-
-    if ( tobj->current_contour == NULL )
-    {
-       /* gluTessBeginContour was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR2 );
-       return;
-    }
-
-    if ( tobj->current_contour->num_vertices > 0 ) {
-       inspect_current_contour( tobj );
-    } else {
-       delete_contour( &tobj->current_contour );
-    }
-
- cleanup:
-    MSG( 15, "  <- gluTessEndContour( tobj:%p )\n", tobj );
-    return;
-}
-
 
-/*****************************************************************************
- * gluTessEndPolygon
- *****************************************************************************/
-void GLAPIENTRY gluTessEndPolygon( GLUtesselator *tobj )
+void GLAPIENTRY
+gluDeleteTess(GLUtriangulatorObj * tobj)
 {
-    MSG( 15, "-> gluTessEndPolygon( tobj:%p )\n", tobj );
-    TESS_CHECK_ERRORS( tobj );
-
-    if ( tobj->current_contour != NULL )
-    {
-       /* gluTessBeginPolygon was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR1 );
-       return;
-    }
-    TESS_CHECK_ERRORS( tobj );
-
-    /*
-     * Ensure we have at least one contour to tessellate.  If we have none,
-     *  clean up and exit gracefully.
-     */
-    if ( tobj->num_contours == 0 ) {
-       tess_cleanup( tobj );
-       return;
-    }
-
-    /* Wrap the contour list. */
-
-    tobj->last_contour->next = tobj->contours;
-    tobj->contours->prev = tobj->last_contour;
-
-    TESS_CHECK_ERRORS( tobj );
-
-    /* Orient the contours correctly */
-    orient_contours( tobj );
-
-    /*
-     * Before we tessellate the contours, ensure we have the appropriate
-     *  callbacks registered.  We at least need the begin, vertex and end
-     *  callbacks to do any meaningful work.
-     */
-    if ( ( ( tobj->callbacks.begin != NULL ) ||
-          ( tobj->callbacks.beginData != NULL ) ) &&
-        ( ( tobj->callbacks.vertex != NULL ) ||
-          ( tobj->callbacks.vertexData != NULL ) ) &&
-        ( ( tobj->callbacks.end != NULL ) ||
-          ( tobj->callbacks.endData != NULL ) ) )
-    {
-       fist_tessellation( tobj );
-    }
-
- cleanup:
-    delete_all_contours( tobj );
-    MSG( 15, "<- gluTessEndPolygon( tobj:%p )\n", 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);
 }
 
 
-/*****************************************************************************
- * gluTessCallback
- *****************************************************************************/
-void GLAPIENTRY gluTessCallback( GLUtesselator *tobj, GLenum which,
-                                void (GLCALLBACKP fn)() )
+void GLAPIENTRY
+gluBeginPolygon(GLUtriangulatorObj * tobj)
 {
-    switch ( which )
-    {
-       /* Register the begin callbacks. */
-    case GLU_TESS_BEGIN:
-       tobj->callbacks.begin = (void (GLCALLBACKPCAST)(GLenum)) fn;
-       break;
-    case GLU_TESS_BEGIN_DATA:
-       tobj->callbacks.beginData = (void (GLCALLBACKPCAST)(GLenum, void *)) fn;
-       break;
-
-       /* Register the edge flag callbacks. */
-    case GLU_TESS_EDGE_FLAG:
-       tobj->callbacks.edgeFlag = (void (GLCALLBACKPCAST)(GLboolean)) fn;
-       break;
-    case GLU_TESS_EDGE_FLAG_DATA:
-       tobj->callbacks.edgeFlagData = (void (GLCALLBACKPCAST)(GLboolean, void *)) fn;
-       break;
-
-       /* Register the vertex callbacks. */
-    case GLU_TESS_VERTEX:
-       tobj->callbacks.vertex = (void (GLCALLBACKPCAST)(void *)) fn;
-       break;
-    case GLU_TESS_VERTEX_DATA:
-       tobj->callbacks.vertexData = (void (GLCALLBACKPCAST)(void *, void *)) fn;
-       break;
-
-       /* Register the end callbacks. */
-    case GLU_TESS_END:
-       tobj->callbacks.end = (void (GLCALLBACKPCAST)(void)) fn;
-       break;
-    case GLU_TESS_END_DATA:
-       tobj->callbacks.endData = (void (GLCALLBACKPCAST)(void *)) fn;
-       break;
-
-       /* Register the error callbacks. */
-    case GLU_TESS_ERROR:
-       tobj->callbacks.error = (void (GLCALLBACKPCAST)(GLenum)) fn;
-       break;
-    case GLU_TESS_ERROR_DATA:
-       tobj->callbacks.errorData = (void (GLCALLBACKPCAST)(GLenum, void *)) fn;
-       break;
-
-       /* Register the combine callbacks. */
-    case GLU_TESS_COMBINE:
-       tobj->callbacks.combine = (void (GLCALLBACKPCAST)(GLdouble[3], void *[4], GLfloat [4], void **)) fn;
-       break;
-    case GLU_TESS_COMBINE_DATA:
-       tobj->callbacks.combineData = (void (GLCALLBACKPCAST)(GLdouble[3], void *[4], GLfloat [4], void **, void *)) fn;
-       break;
-
-    default:
-       MSG( 1, "  gluTessCallback( tobj:%p which:%d ) invalid enum\n", tobj, which );
-       tobj->error = GLU_INVALID_ENUM;
-       break;
-    }
+/*
+       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;
+   }
 }
 
 
-/*****************************************************************************
- * gluTessProperty
- *
- * Set the current value of the given property.
- *****************************************************************************/
-void GLAPIENTRY gluTessProperty( GLUtesselator *tobj, GLenum which,
-                                GLdouble value )
+void GLAPIENTRY
+gluEndPolygon(GLUtriangulatorObj * tobj)
 {
-    switch ( which )
-    {
-    case GLU_TESS_BOUNDARY_ONLY:
-       tobj->boundary_only = (GLboolean) value;
-       break;
-
-    case GLU_TESS_TOLERANCE:
-       MSG( 15, "   gluTessProperty( tobj:%p ) tolerance: %0.9f\n", tobj, value );
-       tobj->tolerance = value;
-       break;
-
-    case GLU_TESS_WINDING_RULE:
-       tobj->winding_rule = (GLenum) value;
-       break;
-
-    default:
-       MSG( 1, "   gluTessProperty( tobj:%p which:%d ) invalid enum\n", tobj, which );
-       tobj->error = GLU_INVALID_ENUM;
-       break;
-    }
+   /*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);
 }
 
 
-/*****************************************************************************
- * gluGetTessProperty
- *
- * Return the current value of the given property.
- *****************************************************************************/
-void GLAPIENTRY gluGetTessProperty( GLUtesselator *tobj, GLenum which,
-                                   GLdouble *value )
+void GLAPIENTRY
+gluNextContour(GLUtriangulatorObj * tobj, GLenum type)
 {
-    switch ( which )
-    {
-    case GLU_TESS_BOUNDARY_ONLY:
-       *value = tobj->boundary_only;
-       break;
-
-    case GLU_TESS_TOLERANCE:
-       *value = tobj->tolerance;
-       break;
-
-    case GLU_TESS_WINDING_RULE:
-       *value = tobj->winding_rule;
-       break;
-
-    default:
-       MSG( 1, "   gluGetTessProperty( tobj:%p which:%d ) invalid enum\n", tobj, which );
-       tobj->error = GLU_INVALID_ENUM;
-       break;
-    }
+   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);
 }
 
 
-/*****************************************************************************
- * gluTessNormal
- *
- * Set the current tessellation normal.
- *****************************************************************************/
-void GLAPIENTRY gluTessNormal( GLUtesselator *tobj, GLdouble x,
-                              GLdouble y, GLdouble z )
+void GLAPIENTRY
+gluTessVertex(GLUtriangulatorObj * tobj, GLdouble v[3], void *data)
 {
-    MSG( 15, "   gluTessNormal( tobj:%p n:(%.2f,%.2f,%.2f) )\n", tobj, x, y, z );
-
-    ASSIGN_3V( tobj->plane.normal, x, y, z );
+   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;
+   }
 }
 
 
-
-/*****************************************************************************
- *
- *                     OBSOLETE TESSELLATION FUNCTIONS
- *
- *****************************************************************************/
-
-void GLAPIENTRY gluBeginPolygon( GLUtesselator *tobj )
+static void
+delete_contours(GLUtriangulatorObj * tobj)
 {
-    gluTessBeginPolygon( tobj, NULL );
-    gluTessBeginContour( 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;
 }
 
-void GLAPIENTRY gluNextContour( GLUtesselator *tobj, GLenum type )
-{
-    gluTessEndContour( tobj );
-    gluTessBeginContour( tobj );
-}
 
-void GLAPIENTRY gluEndPolygon( GLUtesselator *tobj )
+void GLAPIENTRY
+gluTessNormal(GLUtesselator *tess, GLdouble valueX, GLdouble valueY, GLdouble valueZ)
 {
-    gluTessEndContour( tobj );
-    gluTessEndPolygon( tobj );
+   /* dummy function */
+   (void) tess;
+   (void) valueX;
+   (void) valueY;
+   (void) valueZ;
 }