Merging in work from 3.1/3.2 branch. Tessellator is essentially fully
authorGareth Hughes <gareth@valinux.com>
Mon, 6 Dec 1999 09:39:34 +0000 (09:39 +0000)
committerGareth Hughes <gareth@valinux.com>
Mon, 6 Dec 1999 09:39:34 +0000 (09:39 +0000)
functional now.

src/glu/mesa/Makefile.BeOS
src/glu/mesa/Makefile.BeOS-R4
src/glu/mesa/Makefile.X11
src/glu/mesa/tess.c
src/glu/mesa/tess.h

index 989ad7c157d307e4053c56a83494059dcd77f05f..e8851dea444b2062aee31cd111d58b6d2f54d3f7 100644 (file)
@@ -30,7 +30,7 @@ LIBDIR = ../lib
 
 SOURCES = glu.c mipmap.c nurbs.c nurbscrv.c nurbssrf.c nurbsutl.c \
        project.c quadric.c tess.c tess_fist.c tess_hash.c tess_heap.c \
-       tess_winding.c
+       tess_winding.c tess_clip.c
 
 OBJECTS = $(SOURCES:.c=.o)
 
index 1285db4c3332b368dc3e64f1f7cf3d28abe05a3a..ebe3a689d3842285d1a64708d5db16321da8b2df 100644 (file)
 # Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 
-# $Id: Makefile.BeOS-R4,v 1.5 1999/10/03 00:53:38 gareth Exp $
+# $Id: Makefile.BeOS-R4,v 1.6 1999/12/06 09:39:34 gareth Exp $
 
 # $Log: Makefile.BeOS-R4,v $
+# Revision 1.6  1999/12/06 09:39:34  gareth
+# Merging in work from 3.1/3.2 branch.  Tessellator is essentially fully
+# functional now.
+#
 # Revision 1.5  1999/10/03 00:53:38  gareth
 # Added tessellation winding rule files.
 #
@@ -59,7 +63,7 @@ LIBDIR = ../lib
 
 SOURCES = glu.c mipmap.c nurbs.c nurbscrv.c nurbssrf.c nurbsutl.c \
        project.c quadric.c tess.c tess_fist.c tess_hash.c tess_heap.c \
-       tess_winding.c
+       tess_winding.c tess_clip.c
 
 OBJECTS = $(SOURCES:.c=.o)
 
index 3036001776c586273043453d62a03a54cf0116c4..4373c8f2b4dec5df3fe24b52be73acdb35f47ccb 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Makefile.X11,v 1.6 1999/10/03 00:53:38 gareth Exp $
+# $Id: Makefile.X11,v 1.7 1999/12/06 09:39:34 gareth Exp $
 
 # Mesa 3-D graphics library
 # Version:  3.1
@@ -20,7 +20,7 @@ LIBDIR = ../lib
 
 SOURCES = glu.c mipmap.c nurbs.c nurbscrv.c nurbssrf.c nurbsutl.c \
        project.c quadric.c tess.c tess_fist.c tess_hash.c tess_heap.c \
-       tess_winding.c
+       tess_winding.c tess_clip.c
 
 OBJECTS = $(SOURCES:.c=.o)
 
index 7f1b5a568235cac1b2c90e6aff10d0e56a4df129..f48c6a01719afbc3ff5c7d054428078fbcb8b69c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: tess.c,v 1.21 1999/11/11 03:21:43 kendallb Exp $ */
+/* $Id: tess.c,v 1.22 1999/12/06 09:39:34 gareth Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -33,7 +33,9 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <GL/glu.h>
+#include <math.h>
+
+#include "gluP.h"
 
 #include "tess.h"
 #include "tess_macros.h"
 #include "tess_grid.h"
 #endif
 
-/*****************************************************************************
- * Internal function prototypes:
- *****************************************************************************/
-
-static void init_callbacks( tess_callbacks_t *callbacks );
-
-static void tess_cleanup( GLUtesselator *tobj );
-static void inspect_current_contour( GLUtesselator *tobj );
-
-void delete_contour( tess_contour_t **contour );
-static void delete_all_contours( GLUtesselator *tobj );
 
 #define TESS_CHECK_ERRORS(t)   if ( (t)->error != GLU_NO_ERROR ) goto cleanup
 
@@ -62,914 +53,991 @@ GLint             tess_dbg_level;
 
 
 /*****************************************************************************
+ * tess_error_callback
  *
- *                     GLU TESSELLATION FUNCTIONS
+ * 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 );
+    }
+}
 
 
 /*****************************************************************************
- * gluNewTess
+ * init_callbacks
  *****************************************************************************/
-GLUtesselator* GLAPIENTRY gluNewTess( void )
+static void init_callbacks( tess_callbacks_t *callbacks )
 {
-    GLUtesselator *tobj;
+    callbacks->begin        = ( void (GLCALLBACKP)(GLenum) ) NULL;
+    callbacks->beginData    = ( void (GLCALLBACKP)(GLenum, void *) ) NULL;
+    callbacks->edgeFlag     = ( void (GLCALLBACKP)(GLboolean) ) NULL;
+    callbacks->edgeFlagData = ( void (GLCALLBACKP)(GLboolean, void *) ) NULL;
+    callbacks->vertex       = ( void (GLCALLBACKP)(void *) ) NULL;
+    callbacks->vertexData   = ( void (GLCALLBACKP)(void *, void *) ) NULL;
+    callbacks->end          = ( void (GLCALLBACKP)(void) ) NULL;
+    callbacks->endData      = ( void (GLCALLBACKP)(void *) ) NULL;
+    callbacks->error        = ( void (GLCALLBACKP)(GLenum) ) NULL;
+    callbacks->errorData    = ( void (GLCALLBACKP)(GLenum, void *) ) NULL;
+    callbacks->combine      = ( void (GLCALLBACKP)(GLdouble [3], void *[4],
+                                                  GLfloat [4], void **) ) NULL;
+    callbacks->combineData  = ( void (GLCALLBACKP)(GLdouble [3], void *[4],
+                                                  GLfloat [4], void **,
+                                                  void *) ) NULL;
+}
 
-#ifdef DEBUG
-    if ( getenv( "GLU_TESS_DBG_LEVEL" ) ) {
-       tess_dbg_level = atoi( getenv( "GLU_TESS_DBG_LEVEL" ) );
-    } else {
-       tess_dbg_level = DBG_LEVEL_BASE;
-    }
-#endif
 
-    MSG( 15, "-> gluNewTess()\n" );
+/*****************************************************************************
+ * 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];
 
-    tobj = malloc( sizeof(GLUtesselator) );
-    if ( tobj == NULL ) {
-       return NULL;
+    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;
     }
 
-    init_callbacks( &tobj->callbacks );
+    if ( vb == va ) {
+       /* FIXME: What error is this? */
+       tess_error_callback( tobj, GLU_TESS_ERROR7 );
+    }
 
-    tobj->boundary_only = GL_FALSE;
-    tobj->winding_rule = GLU_TESS_WINDING_ODD;
-    tobj->tolerance = 0.0;
+    SUB_3V( a, vb->coords, va->coords );
 
-    tobj->plane.normal[X] = 0.0;
-    tobj->plane.normal[Y] = 0.0;
-    tobj->plane.normal[Z] = 0.0;
-    tobj->plane.dist = 0.0;
+    for ( vc = vb->next; vc != va; vc = vc->next )
+    {
+       SUB_3V( b, vc->coords, va->coords );
 
-    tobj->contour_count = 0;
-    tobj->contours = tobj->last_contour = NULL;
-    tobj->current_contour = NULL;
+       CROSS_3V( c, a, b );
 
-    CLEAR_BBOX_2DV( tobj->mins, tobj->maxs );
+       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] );
 
-    tobj->vertex_count = 0;
-    tobj->sorted_vertices = NULL;
-#if 0
-    tobj->grid = NULL;
-#endif
-    tobj->cvc_lists = NULL;
-    tobj->data = NULL;
-    tobj->edge_flag = GL_FALSE;
-    tobj->label = 0;
+           COPY_3V( contour->plane.normal, c );
+           NORMALIZE_3DV( contour->plane.normal );
 
-    tobj->error = GLU_NO_ERROR;
+           contour->plane.dist = - DOT_3V( contour->plane.normal,
+                                           va->coords );
 
-    MSG( 15, "<- gluNewTess() tobj:%p\n", tobj );
-    return tobj;
-}
+           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;
+}
 
 /*****************************************************************************
- * gluDeleteTess
+ * twice_contour_area
+ *
+ * Calculate the twice the signed area of the given contour.  Used to
+ * determine the contour's orientation amongst other things.
  *****************************************************************************/
-void GLAPIENTRY gluDeleteTess( GLUtesselator *tobj )
+GLdouble twice_contour_area( tess_contour_t *contour )
 {
-    MSG( 15, "-> gluDeleteTess( tobj:%p )\n", tobj );
+    tess_vertex_t      *vertex = contour->vertices;
+    GLdouble           area, x, y;
+
+    area = 0.0;
+
+    x = vertex->v[X];
+    y = vertex->v[Y];
+
+    vertex = vertex->next;
 
-    if ( ( tobj->error == GLU_NO_ERROR ) && ( tobj->contour_count > 0 ) )
+    do
     {
-       /* gluEndPolygon was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR3 );
+       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 );
 
-    /* Delete all internal structures. */
-    tess_cleanup( tobj );
-    free( tobj );
-
-    MSG( 15, "<- gluDeleteTess()\n" );
+    return area;
 }
 
-
 /*****************************************************************************
- * gluTessBeginPolygon
+ * 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.
  *****************************************************************************/
-void GLAPIENTRY gluTessBeginPolygon( GLUtesselator *tobj, void *polygon_data )
+static void project_current_contour( GLUtesselator *tobj )
 {
-    MSG( 15, "-> gluTessBeginPolygon( tobj:%p data:%p )\n", tobj, polygon_data );
+    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;
 
-    tobj->error = GLU_NO_ERROR;
+    MSG( 15, "      --> project_current_contour( tobj:%p )\n", tobj );
 
-    if ( tobj->current_contour != NULL )
+    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++ )
     {
-       /* gluEndPolygon was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR3 );
-       tess_cleanup( tobj );
+       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 );
     }
 
-    tobj->vertex_count = 0;
-    tobj->data = polygon_data;
-    tobj->edge_flag = GL_FALSE;
-    tobj->label = 0;
+    current->area = twice_contour_area( current );
+    current->orientation = ( current->area >= 0.0 ) ? GLU_CCW : GLU_CW;
 
-    MSG( 15, "<- gluTessBeginPolygon( tobj:%p data:%p )\n", tobj, polygon_data );
-}
+    MSG( 15, "            area: %.2f orientation: %s\n",
+        current->area, ( current->orientation == GLU_CCW ) ? "CCW" : "CW" );
 
+    MSG( 15, "      <-- project_current_contour( tobj:%p )\n", tobj );
+}
 
 /*****************************************************************************
- * gluTessBeginContour
+ * save_current_contour
  *****************************************************************************/
-void GLAPIENTRY gluTessBeginContour( GLUtesselator *tobj )
+static GLenum save_current_contour( GLUtesselator *tobj )
 {
-    MSG( 15, "  -> gluTessBeginContour( tobj:%p )\n", tobj );
-    TESS_CHECK_ERRORS( tobj );
+    tess_contour_t     *current = tobj->current_contour;
+    tess_vertex_t      *vertex;
+    GLint              i;
 
-    if ( tobj->current_contour != NULL )
+    if ( current == NULL ) { return GLU_ERROR; }
+
+    if ( tobj->contours == NULL )
     {
-       /* gluTessEndContour was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR4 );
-       return;
-    }
+       tobj->contours = tobj->last_contour = current;
+       current->next = current->prev = NULL;
 
-    tobj->current_contour = malloc( sizeof(tess_contour_t) );
-    if ( tobj->current_contour == NULL ) {
-       tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
-       return;
+       tobj->orientation = current->orientation;
     }
+    else
+    {
+       current->prev = tobj->last_contour;
 
-    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->last_contour->next = current;
+       tobj->last_contour = current;
 
-    tobj->current_contour->rotx = tobj->current_contour->roty = 0.0;
+       current->next = NULL;
+    }
 
-    CLEAR_BBOX_2DV( tobj->current_contour->mins,
-                   tobj->current_contour->maxs );
+    for ( vertex = current->vertices, i = 0;
+         i < current->num_vertices; vertex = vertex->next, i++ )
+    {
+       vertex->edge_flag = GL_TRUE;
+    }
 
-    tobj->current_contour->vertex_count = 0;
-    tobj->current_contour->vertices =
-       tobj->current_contour->last_vertex = NULL;
+    current->type = GLU_UNKNOWN;
 
-    tobj->current_contour->reflex_vertices = NULL;
-    tobj->current_contour->cross_vertices = hashtable_init( HT_DEFAULT_SIZE );
+    tobj->num_contours++;
+    tobj->current_contour = NULL;
 
- cleanup:
-    MSG( 15, "  <- gluTessBeginContour( tobj:%p )\n", tobj );
-    return;
+    return GLU_NO_ERROR;
 }
 
-
 /*****************************************************************************
- * gluTessVertex
+ * inspect_current_contour
  *****************************************************************************/
-void GLAPIENTRY gluTessVertex( GLUtesselator *tobj, GLdouble coords[3],
-                              void *vertex_data )
+static void inspect_current_contour( GLUtesselator *tobj )
 {
-    tess_contour_t             *current = tobj->current_contour;
-    tess_vertex_t              *last_vertex;
+    tess_contour_t     *current = tobj->current_contour;
+    GLdouble           origin[3] = { 0.0, 0.0, 0.0 };
+    GLboolean          calc_normal = GL_FALSE;
 
-    MSG( 15, "    -> gluTessVertex( tobj:%p coords:(%.2f,%.2f,%.2f) )\n", tobj, coords[0], coords[1], coords[2] );
-    TESS_CHECK_ERRORS( tobj );
+    MSG( 15, "    --> inspect_current_contour( tobj:%p )\n", tobj );
 
-    if ( current == NULL )
+    if ( current->num_vertices < 3 )
     {
-       /* gluTessBeginContour was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR2 );
+       MSG( 15, "          count %d < 3, deleting\n", current->num_vertices );
+       delete_contour( &tobj->current_contour );
        return;
     }
 
-    tobj->vertex_count++;
+    current->last_vertex->next = current->vertices;
+    current->vertices->prev = current->last_vertex;
 
-    last_vertex = current->last_vertex;
+    MSG( 15, "          current normal: (%.2f, %.2f, %.2f)\n", current->plane.normal[X], current->plane.normal[Y], current->plane.normal[Z] );
 
-    if ( last_vertex == NULL )
+    if ( IS_EQUAL_3DV( current->plane.normal, origin ) )
     {
-       last_vertex = malloc( sizeof(tess_vertex_t) );
-       if ( last_vertex == NULL ) {
-           tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
+       /* We haven't been given a normal, so let's take a guess. */
+       if ( find_normal( tobj ) == GLU_ERROR ) {
            return;
        }
 
-       current->vertices = last_vertex;
-       current->last_vertex = last_vertex;
+       COPY_3V( tobj->plane.normal, current->plane.normal );
+       tobj->plane.dist = current->plane.dist;
 
-       last_vertex->index = -1;
-       last_vertex->data = vertex_data;
+       calc_normal = GL_TRUE;
+    }
 
-       last_vertex->coords[X] = coords[X];
-       last_vertex->coords[Y] = coords[Y];
-       last_vertex->coords[Z] = coords[Z];
+    project_current_contour( tobj );
 
-       last_vertex->side = 0.0;
-       last_vertex->label = 0;
-       last_vertex->mark = 0;
+    if ( calc_normal && ( tobj->current_contour->orientation == GLU_CW ) )
+    {
+       MSG( 15, "          oops, let's try that again...\n" );
 
-       last_vertex->next = NULL;
-       last_vertex->previous = NULL;
+       /*
+        * 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 );
 
-       current->vertex_count++;
+       project_current_contour( tobj );
     }
-    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;
+    if ( save_current_contour( tobj ) == GLU_ERROR ) {
+       return;
+    }
 
-       vertex->coords[X] = coords[X];
-       vertex->coords[Y] = coords[Y];
-       vertex->coords[Z] = coords[Z];
+    MSG( 15, "    <-- inspect_current_contour( tobj:%p )\n", tobj );
+}
 
-       vertex->side = 0.0;
-       vertex->label = 0;
-       vertex->mark = 0;
+/*****************************************************************************
+ * reverse_contour
+ *****************************************************************************/
+void reverse_contour( tess_contour_t *contour )
+{
+    tess_vertex_t      *current = contour->vertices;
+    GLint              i;
 
-       vertex->next = NULL;
-       vertex->previous = last_vertex;
+    for ( i = 0 ; i < contour->num_vertices ; i++ )
+    {
+       tess_vertex_t   *next = current->next;
+       tess_vertex_t   *prev = current->prev;
 
-       current->vertex_count++;
+       current->next = prev;
+       current->prev = next;
 
-       last_vertex->next = vertex;
-       current->last_vertex = vertex;
+       current = next;
     }
 
- cleanup:
-    MSG( 15, "    <- gluTessVertex( tobj:%p )\n", tobj );
-    return;
-}
+    contour->orientation =
+       ( contour->orientation == GLU_CCW ) ? GLU_CW : GLU_CCW;
 
+    contour->last_vertex = contour->vertices->prev;
+}
 
 /*****************************************************************************
- * gluTessEndContour
+ * orient_contours
+ *
+ * Sum the signed areas of the contours, and orient the contours such that
+ * this sum is nonnegative.
  *****************************************************************************/
-void GLAPIENTRY gluTessEndContour( GLUtesselator *tobj )
+static void orient_contours( GLUtesselator *tobj )
 {
-    MSG( 15, "  -> gluTessEndContour( tobj:%p )\n", tobj );
-    TESS_CHECK_ERRORS( tobj );
+    tess_contour_t     *contour = tobj->contours;
+    GLdouble           sum = 0.0;
+    GLint              i;
 
-    if ( tobj->current_contour == NULL )
+    MSG( 15, "    --> orient_contours( tobj:%p )\n", tobj );
+
+    /* Sum the signed areas of all contours */
+    for ( i = 0 ; i < tobj->num_contours ; i++ )
     {
-       /* gluTessBeginContour was not called. */
-       tess_error_callback( tobj, GLU_TESS_ERROR2 );
-       return;
+       sum += contour->area;
+       contour = contour->next;
     }
 
-    if ( tobj->current_contour->vertex_count > 0 ) {
-       inspect_current_contour( tobj );
-    } else {
-       delete_contour( &tobj->current_contour );
+    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 );
 
- cleanup:
-    MSG( 15, "  <- gluTessEndContour( tobj:%p )\n", tobj );
-    return;
+           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" );
 }
 
 
 /*****************************************************************************
- * gluTessEndPolygon
+ * delete_contour
+ *
+ * Delete the given contour and set the pointer to NULL.
  *****************************************************************************/
-void GLAPIENTRY gluTessEndPolygon( GLUtesselator *tobj )
+void delete_contour( tess_contour_t **contour )
 {
-    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->contour_count == 0 ) {
-       tess_cleanup( tobj );
-       return;
-    }
-
-    /* Wrap the contour list. */
+    tess_vertex_t      *vertex, *next;
+    GLint              i;
 
-    tobj->last_contour->next = tobj->contours;
-    tobj->contours->previous = tobj->last_contour;
+    if ( *contour == NULL ) { return; }
 
-    TESS_CHECK_ERRORS( tobj );
+    vertex = (*contour)->vertices;
 
-    /*
-     * 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 ) ) )
+    for ( i = 0 ; i < (*contour)->num_vertices ; i++ )
     {
-       fist_tessellation( tobj );
+       next = vertex->next;
+       free( vertex );
+       vertex = next;
     }
 
- cleanup:
-    delete_all_contours( tobj );
-    MSG( 15, "<- gluTessEndPolygon( tobj:%p )\n", tobj );
+    free( *contour );
+    *contour = NULL;
 }
 
-
 /*****************************************************************************
- * gluTessCallback
+ * delete_all_contours
  *****************************************************************************/
-void GLAPIENTRY gluTessCallback( GLUtesselator *tobj, GLenum which,
-                                void (GLCALLBACKP fn)() )
+static void delete_all_contours( GLUtesselator *tobj )
 {
-    switch ( which )
+    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++ )
     {
-       /* Register the begin callbacks. */
-    case GLU_TESS_BEGIN:
-       tobj->callbacks.begin = (void (GLCALLBACKP)(GLenum)) fn;
-       break;
-    case GLU_TESS_BEGIN_DATA:
-       tobj->callbacks.beginData = (void (GLCALLBACKP)(GLenum, void *)) fn;
-       break;
+       tess_vertex_t   *vertex = current->vertices, *next_vertex;
+       GLint           j;
 
-       /* Register the edge flag callbacks. */
-    case GLU_TESS_EDGE_FLAG:
-       tobj->callbacks.edgeFlag = (void (GLCALLBACKP)(GLboolean)) fn;
-       break;
-    case GLU_TESS_EDGE_FLAG_DATA:
-       tobj->callbacks.edgeFlagData =
-               (void (GLCALLBACKP)(GLboolean, void *)) fn;
-       break;
+       for ( j = 0 ; j < current->num_vertices ; j ++ )
+       {
+           next_vertex = vertex->next;
+           free( vertex );
+           vertex = next_vertex;
+       }
+       next_contour = current->next;
 
-       /* Register the vertex callbacks. */
-    case GLU_TESS_VERTEX:
-       tobj->callbacks.vertex = (void (GLCALLBACKP)(void *)) fn;
-       break;
-    case GLU_TESS_VERTEX_DATA:
-       tobj->callbacks.vertexData = (void (GLCALLBACKP)(void *, void *)) fn;
-       break;
+       free( current );
+       current = next_contour;
+    }
 
-       /* Register the end callbacks. */
-    case GLU_TESS_END:
-       tobj->callbacks.end = (void (GLCALLBACKP)(void)) fn;
-       break;
-    case GLU_TESS_END_DATA:
-       tobj->callbacks.endData = (void (GLCALLBACKP)(void *)) fn;
-       break;
+    tobj->num_contours = tobj->num_vertices = 0;
+    tobj->contours = tobj->last_contour = NULL;
 
-       /* Register the error callbacks. */
-    case GLU_TESS_ERROR:
-       tobj->callbacks.error = (void (GLCALLBACKP)(GLenum)) fn;
-       break;
-    case GLU_TESS_ERROR_DATA:
-       tobj->callbacks.errorData = (void (GLCALLBACKP)(GLenum, void *)) fn;
-       break;
+    CLEAR_BBOX_2DV( tobj->mins, tobj->maxs );
+}
 
-       /* Register the combine callbacks. */
-    case GLU_TESS_COMBINE:
-       tobj->callbacks.combine =
-               (void (GLCALLBACKP)(GLdouble[3], void *[4],
-                               GLfloat [4], void **)) fn;
-       break;
-    case GLU_TESS_COMBINE_DATA:
-       tobj->callbacks.combineData =
-               (void (GLCALLBACKP)(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;
+/*****************************************************************************
+ * 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 );
 }
 
 
 /*****************************************************************************
- * gluTessProperty
- *
- * Set the current value of the given property.
+ * tess_msg
  *****************************************************************************/
-void GLAPIENTRY gluTessProperty( GLUtesselator *tobj, GLenum which,
-                                GLdouble value )
+INLINE void tess_msg( GLint level, char *format, ... )
 {
-    switch ( which )
-    {
-    case GLU_TESS_BOUNDARY_ONLY:
-       tobj->boundary_only = (GLboolean) value;
-       break;
+#ifdef DEBUG
+    va_list ap;
+    va_start( ap, format );
 
-    case GLU_TESS_TOLERANCE:
-       MSG( 15, "   gluTessProperty( tobj:%p ) tolerance: %0.9f\n", tobj, value );
-       tobj->tolerance = value;
-       break;
+    if ( level <= tess_dbg_level ) {
+       vfprintf( DBG_STREAM, format, ap );
+       fflush( DBG_STREAM );
+    }
 
-    case GLU_TESS_WINDING_RULE:
-       tobj->winding_rule = (GLenum) value;
-       break;
+    va_end( ap );
+#endif
+}
 
-    default:
-       MSG( 1, "   gluTessProperty( tobj:%p which:%d ) invalid enum\n", tobj, which );
-       tobj->error = GLU_INVALID_ENUM;
-       break;
-    }
+INLINE void tess_info( char *file, GLint line )
+{
+#ifdef DEBUG
+    fprintf( DBG_STREAM, "%9.9s:%d:\t ", file, line );
+#endif
 }
 
 
+
 /*****************************************************************************
- * gluGetTessProperty
  *
- * Return the current value of the given property.
+ *                     GLU TESSELLATION FUNCTIONS
+ *
  *****************************************************************************/
-void GLAPIENTRY gluGetTessProperty( GLUtesselator *tobj, GLenum which,
-                                   GLdouble *value )
+
+
+/*****************************************************************************
+ * gluNewTess
+ *****************************************************************************/
+GLUtesselator* GLAPIENTRY gluNewTess( void )
 {
-    switch ( which )
-    {
-    case GLU_TESS_BOUNDARY_ONLY:
-       *value = tobj->boundary_only;
-       break;
+    GLUtesselator *tobj;
 
-    case GLU_TESS_TOLERANCE:
-       *value = tobj->tolerance;
-       break;
+#ifdef DEBUG
+    if ( getenv( "GLU_TESS_DBG_LEVEL" ) ) {
+       tess_dbg_level = atoi( getenv( "GLU_TESS_DBG_LEVEL" ) );
+    } else {
+       tess_dbg_level = DBG_LEVEL_BASE;
+    }
+#endif
 
-    case GLU_TESS_WINDING_RULE:
-       *value = tobj->winding_rule;
-       break;
+    MSG( 15, "-> gluNewTess()\n" );
 
-    default:
-       MSG( 1, "   gluGetTessProperty( tobj:%p which:%d ) invalid enum\n", tobj, which );
-       tobj->error = GLU_INVALID_ENUM;
-       break;
+    tobj = malloc( sizeof(GLUtesselator) );
+    if ( tobj == NULL ) {
+       return NULL;
     }
-}
 
+    init_callbacks( &tobj->callbacks );
 
-/*****************************************************************************
- * gluTessNormal
- *
- * Set the current tessellation normal.
- *****************************************************************************/
-void GLAPIENTRY gluTessNormal( GLUtesselator *tobj, GLdouble x,
-                              GLdouble y, GLdouble z )
-{
-    MSG( 15, "   gluTessNormal( tobj:%p n:(%.2f,%.2f,%.2f) )\n", tobj, x, y, z );
+    tobj->winding_rule = GLU_TESS_WINDING_ODD;
+    tobj->boundary_only = GL_FALSE;
+    tobj->tolerance = GLU_TESS_EPSILON;
+    tobj->orientation = GLU_UNKNOWN;
 
-    tobj->plane.normal[X] = x;
-    tobj->plane.normal[Y] = y;
-    tobj->plane.normal[Z] = z;
-}
+    tobj->data = NULL;
 
+    tobj->num_contours = 0;
+    tobj->contours = tobj->last_contour = NULL;
+    tobj->current_contour = NULL;
 
+    CLEAR_BBOX_2DV( tobj->mins, tobj->maxs );
 
-/*****************************************************************************
- *
- *                     OBSOLETE TESSELLATION FUNCTIONS
- *
- *****************************************************************************/
+    tobj->num_vertices = 0;
+    tobj->sorted_vertices = NULL;
+#if 0
+    tobj->grid = NULL;
+#endif
+    tobj->edge_flag = GL_FALSE;
+    tobj->label = 0;
 
-void GLAPIENTRY gluBeginPolygon( GLUtesselator *tobj )
-{
-    gluTessBeginPolygon( tobj, NULL );
-    gluTessBeginContour( tobj );
-}
+    ZERO_3V( tobj->plane.normal );
+    tobj->plane.dist = 0.0;
 
-void GLAPIENTRY gluNextContour( GLUtesselator *tobj, GLenum type )
-{
-    gluTessEndContour( tobj );
-    gluTessBeginContour( tobj );
-}
+    tobj->error = GLU_NO_ERROR;
 
-void GLAPIENTRY gluEndPolygon( GLUtesselator *tobj )
-{
-    gluTessEndContour( tobj );
-    gluTessEndPolygon( tobj );
+    MSG( 15, "<- gluNewTess() tobj:%p\n", tobj );
+    return tobj;
 }
 
 
-
 /*****************************************************************************
- * 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
+ * gluDeleteTess
  *****************************************************************************/
-
-void tess_error_callback( GLUtesselator *tobj, GLenum errnum )
+void GLAPIENTRY gluDeleteTess( GLUtesselator *tobj )
 {
-    if ( tobj->error == GLU_NO_ERROR )
-    {
-       tobj->error = errnum;
-    }
+    MSG( 15, "-> gluDeleteTess( tobj:%p )\n", tobj );
 
-    if ( tobj->callbacks.errorData != NULL )
-    {
-       ( tobj->callbacks.errorData )( errnum, tobj->data );
-    }
-    else if ( tobj->callbacks.error != NULL )
+    if ( ( tobj->error == GLU_NO_ERROR ) && ( tobj->num_contours > 0 ) )
     {
-       ( tobj->callbacks.error )( errnum );
+       /* gluEndPolygon was not called. */
+       tess_error_callback( tobj, GLU_TESS_ERROR3 );
     }
-}
-
 
+    /* Delete all internal structures. */
+    tess_cleanup( tobj );
+    free( tobj );
 
-/*****************************************************************************
- *
- *                             INTERNAL FUNCTIONS
- *
- *****************************************************************************/
+    MSG( 15, "<- gluDeleteTess()\n" );
+}
 
 
 /*****************************************************************************
- * init_callbacks
+ * gluTessBeginPolygon
  *****************************************************************************/
-static void init_callbacks( tess_callbacks_t *callbacks )
+void GLAPIENTRY gluTessBeginPolygon( GLUtesselator *tobj, void *polygon_data )
 {
-       callbacks->begin        = ( void (GLCALLBACKP)(GLenum) ) NULL;
-       callbacks->beginData    = ( void (GLCALLBACKP)(GLenum, void *) ) NULL;
-       callbacks->edgeFlag     = ( void (GLCALLBACKP)(GLboolean) ) NULL;
-       callbacks->edgeFlagData = ( void (GLCALLBACKP)(GLboolean, void *) ) NULL;
-       callbacks->vertex       = ( void (GLCALLBACKP)(void *) ) NULL;
-       callbacks->vertexData   = ( void (GLCALLBACKP)(void *, void *) ) NULL;
-       callbacks->end          = ( void (GLCALLBACKP)(void) ) NULL;
-       callbacks->endData      = ( void (GLCALLBACKP)(void *) ) NULL;
-       callbacks->error        = ( void (GLCALLBACKP)(GLenum) ) NULL;
-       callbacks->errorData    = ( void (GLCALLBACKP)(GLenum, void *) ) NULL;
-       callbacks->combine      = ( void (GLCALLBACKP)(GLdouble [3], void *[4],
-                                                  GLfloat [4], void **) ) NULL;
-       callbacks->combineData  = ( void (GLCALLBACKP)(GLdouble [3], void *[4],
-                                                  GLfloat [4], void **,
-                                                  void *) ) NULL;
+    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 );
 }
 
 
 /*****************************************************************************
- * tess_cleanup
+ * gluTessBeginContour
  *****************************************************************************/
-static void tess_cleanup( GLUtesselator *tobj )
+void GLAPIENTRY gluTessBeginContour( GLUtesselator *tobj )
 {
-    MSG( 15, "  -> tess_cleanup( tobj:%p )\n", tobj );
+    MSG( 15, "  -> gluTessBeginContour( tobj:%p )\n", tobj );
+    TESS_CHECK_ERRORS( tobj );
 
-    if ( tobj->current_contour != NULL ) {
-       delete_contour( &tobj->current_contour );
+    if ( tobj->current_contour != NULL )
+    {
+       /* gluTessEndContour was not called. */
+       tess_error_callback( tobj, GLU_TESS_ERROR4 );
+       return;
     }
-    if ( tobj->contours != NULL ) {
-       delete_all_contours( tobj );
+
+    tobj->current_contour = malloc( sizeof(tess_contour_t) );
+    if ( tobj->current_contour == NULL ) {
+       tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
+       return;
     }
 
-    MSG( 15, "  <- tess_cleanup( tobj:%p )\n", tobj );
+    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;
 }
 
 
 /*****************************************************************************
- * inspect_current_contour
+ * gluTessVertex
  *****************************************************************************/
-static GLenum  find_normal( GLUtesselator *tobj );
-static void    project_current_contour( GLUtesselator *tobj );
-static GLenum  save_current_contour( GLUtesselator *tobj );
-
-static void inspect_current_contour( GLUtesselator *tobj )
+void GLAPIENTRY gluTessVertex( GLUtesselator *tobj, GLdouble coords[3],
+                              void *vertex_data )
 {
-    tess_contour_t *current = tobj->current_contour;
-    GLdouble origin[3] = { 0.0, 0.0, 0.0 };
+    tess_contour_t             *current = tobj->current_contour;
+    tess_vertex_t              *last_vertex;
 
-    MSG( 15, "    -> inspect_current_contour( tobj:%p )\n", tobj );
+    MSG( 15, "    -> gluTessVertex( tobj:%p coords:(%.2f,%.2f,%.2f) )\n", tobj, coords[0], coords[1], coords[2] );
+    TESS_CHECK_ERRORS( tobj );
 
-    if ( current->vertex_count < 3 )
+    if ( current == NULL )
     {
-       MSG( 15, "         count %d < 3, deleting\n", current->vertex_count );
-       delete_contour( &tobj->current_contour );
+       /* gluTessBeginContour was not called. */
+       tess_error_callback( tobj, GLU_TESS_ERROR2 );
        return;
     }
 
-    current->last_vertex->next = current->vertices;
-    current->vertices->previous = current->last_vertex;
+    tobj->num_vertices++;
+
+    last_vertex = current->last_vertex;
 
-    if ( ( tobj->contours == NULL ) &&
-        ( COMPARE_3DV( current->plane.normal, origin ) ) )
+    if ( last_vertex == NULL )
     {
-       /* We haven't been given a normal, so let's take a guess. */
-       if ( find_normal( tobj ) == GLU_ERROR ) {
+       last_vertex = malloc( sizeof(tess_vertex_t) );
+       if ( last_vertex == NULL ) {
+           tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
            return;
        }
-       COPY_3V( tobj->plane.normal, current->plane.normal );
-       tobj->plane.dist = current->plane.dist;
-    }
-    else
-    {
-       MSG( 15, "         normal: (%.2f,%.2f,%.2f)\n", tobj->plane.normal[X], tobj->plane.normal[Y], tobj->plane.normal[Z] );
-    }
-
-    project_current_contour( tobj );
 
-    if ( save_current_contour( tobj ) == GLU_ERROR ) {
-       return;
-    }
+       current->vertices = last_vertex;
+       current->last_vertex = last_vertex;
 
-    MSG( 15, "    <- inspect_current_contour( tobj:%p )\n", tobj );
-}
+       last_vertex->index = -1;
+       last_vertex->data = vertex_data;
 
-/*****************************************************************************
- * 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];
+       last_vertex->coords[X] = coords[X];
+       last_vertex->coords[Y] = coords[Y];
+       last_vertex->coords[Z] = coords[Z];
 
-    MSG( 15, "      -> find_normal( tobj:%p )\n", tobj );
+       last_vertex->v[X] = 0.0;
+       last_vertex->v[Y] = 0.0;
 
-    if ( contour == NULL ) { return GLU_ERROR; }
+       last_vertex->edge_flag = GL_TRUE;
 
-    va = contour->vertices;
-    vb = va->next;
+       last_vertex->side = 0.0;
 
-    /* If va and vb are the same point, keep looking for a different vertex. */
+       last_vertex->next = NULL;
+       last_vertex->prev = NULL;
 
-    while ( COMPARE_3DV( va->coords, vb->coords ) && ( vb != va ) ) {
-       vb = vb->next;
+       current->num_vertices++;
     }
+    else
+    {
+       tess_vertex_t   *vertex;
 
-    if ( vb == va ) {
-       /* FIXME: What error is this? */
-       tess_error_callback( tobj, GLU_TESS_ERROR7 );
-    }
+       vertex = malloc( sizeof(tess_vertex_t) );
+       if ( vertex == NULL ) {
+           tess_error_callback( tobj, GLU_OUT_OF_MEMORY );
+           return;
+       }
 
-    SUB_3V( a, vb->coords, va->coords );
+       vertex->index = -1;
+       vertex->data = vertex_data;
 
-    for ( vc = vb->next; vc != va; vc = vc->next )
-    {
-       SUB_3V( b, vc->coords, va->coords );
+       vertex->coords[X] = coords[X];
+       vertex->coords[Y] = coords[Y];
+       vertex->coords[Z] = coords[Z];
 
-       CROSS3( c, a, b );
+       vertex->v[X] = 0.0;
+       vertex->v[Y] = 0.0;
 
-       if ( ( ABSD( c[X] ) > EQUAL_EPSILON ) ||
-            ( ABSD( c[Y] ) > EQUAL_EPSILON ) ||
-            ( ABSD( c[Z] ) > EQUAL_EPSILON ) )
-       {
-           COPY_3V( contour->plane.normal, c );
-           NORMALIZE_3DV( contour->plane.normal );
+       vertex->edge_flag = GL_TRUE;
 
-           contour->plane.dist = - DOT3( contour->plane.normal, va->coords );
+       vertex->side = 0.0;
 
-           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;
-       }
+       vertex->next = NULL;
+       vertex->prev = last_vertex;
+
+       current->num_vertices++;
+
+       last_vertex->next = vertex;
+       current->last_vertex = vertex;
     }
-    /* FIXME: What error is this? */
-    tess_error_callback( tobj, GLU_TESS_ERROR7 );
 
-    return GLU_ERROR;
+ cleanup:
+    MSG( 15, "    <- gluTessVertex( tobj:%p )\n", tobj );
+    return;
 }
 
+
 /*****************************************************************************
- * project_current_contour
+ * gluTessEndContour
  *****************************************************************************/
-static GLdouble twice_contour_area( tess_vertex_t *vertex,
-                                   tess_vertex_t *last_vertex );
-
-static void project_current_contour( GLUtesselator *tobj )
+void GLAPIENTRY gluTessEndContour( GLUtesselator *tobj )
 {
-    tess_contour_t     *current = tobj->current_contour;
-    tess_vertex_t      *vertex;
-    GLdouble           area;
-    GLdouble           zaxis[3] = { 0.0, 0.0, 1.0 }, znormal[3], xnormal[3];
-    GLdouble           dot, rotx, roty;
-    GLuint             i;
-
-    MSG( 15, "      -> project_current_contour( tobj:%p )\n", tobj );
-
-    if ( current == NULL ) { return; }
-
-    /* Rotate the plane normal around the y-axis. */
+    MSG( 15, "  -> gluTessEndContour( tobj:%p )\n", tobj );
+    TESS_CHECK_ERRORS( tobj );
 
-    znormal[X] = current->plane.normal[X];
-    znormal[Y] = 0.0;
-    znormal[Z] = current->plane.normal[Z];
+    if ( tobj->current_contour == NULL )
+    {
+       /* gluTessBeginContour was not called. */
+       tess_error_callback( tobj, GLU_TESS_ERROR2 );
+       return;
+    }
 
-    dot = DOT3( znormal, zaxis );
-    current->roty = roty = acos( dot );
+    if ( tobj->current_contour->num_vertices > 0 ) {
+       inspect_current_contour( tobj );
+    } else {
+       delete_contour( &tobj->current_contour );
+    }
 
-    /* Rotate the plane normal around the x-axis. */
+ cleanup:
+    MSG( 15, "  <- gluTessEndContour( tobj:%p )\n", tobj );
+    return;
+}
 
-    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 = DOT3( xnormal, zaxis );
-    current->rotx = rotx = acos( dot );
+/*****************************************************************************
+ * gluTessEndPolygon
+ *****************************************************************************/
+void GLAPIENTRY gluTessEndPolygon( GLUtesselator *tobj )
+{
+    MSG( 15, "-> gluTessEndPolygon( tobj:%p )\n", tobj );
+    TESS_CHECK_ERRORS( tobj );
 
-    for ( vertex = current->vertices, i = 0;
-         i < current->vertex_count; vertex = vertex->next, i++ )
+    if ( tobj->current_contour != NULL )
     {
-       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. */
+       /* gluTessBeginPolygon was not called. */
+       tess_error_callback( tobj, GLU_TESS_ERROR1 );
+       return;
+    }
+    TESS_CHECK_ERRORS( tobj );
 
-       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];
+    /*
+     * 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;
+    }
 
-       yrot[X] = cos( roty ) * proj[X] - sin( roty ) * proj[Z];
-       yrot[Y] = proj[Y];
-       yrot[Z] = sin( roty ) * proj[X] + cos( roty ) * proj[Z];
+    /* Wrap the contour list. */
 
-       xrot[X] = yrot[X];
-       xrot[Y] = cos( rotx ) * yrot[Y] - sin( rotx ) * yrot[Z];
-       xrot[Z] = sin( rotx ) * yrot[Y] + cos( rotx ) * yrot[Z];
+    tobj->last_contour->next = tobj->contours;
+    tobj->contours->prev = tobj->last_contour;
 
-       vertex->v[X] = xrot[X];
-       vertex->v[Y] = xrot[Y];
+    TESS_CHECK_ERRORS( tobj );
 
-       ACC_BBOX_2V( vertex->v, tobj->mins, tobj->maxs );
-       ACC_BBOX_2V( vertex->v, current->mins, current->maxs );
-    }
+    /* Orient the contours correctly */
+    orient_contours( tobj );
 
-    area = twice_contour_area( current->vertices,
-                              current->last_vertex );
-    if ( area >= 0.0 )
-    {
-       current->orientation = GLU_CCW;
-       current->area = area;
-    }
-    else
+    /*
+     * 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 ) ) )
     {
-       current->orientation = GLU_CW;
-       current->area = -area;
+       fist_tessellation( tobj );
     }
 
-    MSG( 15, "      <- project_current_contour( tobj:%p )\n", tobj );
+ cleanup:
+    delete_all_contours( tobj );
+    MSG( 15, "<- gluTessEndPolygon( tobj:%p )\n", tobj );
 }
 
+
 /*****************************************************************************
- * twice_contour_area
+ * gluTessCallback
  *****************************************************************************/
-static GLdouble twice_contour_area( tess_vertex_t *vertex,
-                                   tess_vertex_t *last_vertex )
+void GLAPIENTRY gluTessCallback( GLUtesselator *tobj, GLenum which,
+                                void (GLCALLBACKP fn)() )
 {
-    tess_vertex_t      *next;
-    GLdouble           area, x, y;
+    switch ( which )
+    {
+       /* Register the begin callbacks. */
+    case GLU_TESS_BEGIN:
+       tobj->callbacks.begin = (void (GLCALLBACKP)(GLenum)) fn;
+       break;
+    case GLU_TESS_BEGIN_DATA:
+       tobj->callbacks.beginData = (void (GLCALLBACKP)(GLenum, void *)) fn;
+       break;
 
-    area = 0.0;
+       /* Register the edge flag callbacks. */
+    case GLU_TESS_EDGE_FLAG:
+       tobj->callbacks.edgeFlag = (void (GLCALLBACKP)(GLboolean)) fn;
+       break;
+    case GLU_TESS_EDGE_FLAG_DATA:
+       tobj->callbacks.edgeFlagData =
+           (void (GLCALLBACKP)(GLboolean, void *)) fn;
+       break;
 
-    x = vertex->v[X];
-    y = vertex->v[Y];
+       /* Register the vertex callbacks. */
+    case GLU_TESS_VERTEX:
+       tobj->callbacks.vertex = (void (GLCALLBACKP)(void *)) fn;
+       break;
+    case GLU_TESS_VERTEX_DATA:
+       tobj->callbacks.vertexData = (void (GLCALLBACKP)(void *, void *)) fn;
+       break;
 
-    vertex = vertex->next;
+       /* Register the end callbacks. */
+    case GLU_TESS_END:
+       tobj->callbacks.end = (void (GLCALLBACKP)(void)) fn;
+       break;
+    case GLU_TESS_END_DATA:
+       tobj->callbacks.endData = (void (GLCALLBACKP)(void *)) fn;
+       break;
 
-    while ( vertex != last_vertex )
-    {
-       next = vertex->next;
-       area +=
-           (vertex->v[X] - x) * (next->v[Y] - y) -
-           (vertex->v[Y] - y) * (next->v[X] - x);
+       /* Register the error callbacks. */
+    case GLU_TESS_ERROR:
+       tobj->callbacks.error = (void (GLCALLBACKP)(GLenum)) fn;
+       break;
+    case GLU_TESS_ERROR_DATA:
+       tobj->callbacks.errorData = (void (GLCALLBACKP)(GLenum, void *)) fn;
+       break;
 
-       vertex = vertex->next;
+       /* Register the combine callbacks. */
+    case GLU_TESS_COMBINE:
+       tobj->callbacks.combine =
+           (void (GLCALLBACKP)(GLdouble[3], void *[4],
+                               GLfloat [4], void **)) fn;
+       break;
+    case GLU_TESS_COMBINE_DATA:
+       tobj->callbacks.combineData =
+           (void (GLCALLBACKP)(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;
     }
-    return area;
 }
 
 
 /*****************************************************************************
- * save_current_contour
+ * gluTessProperty
+ *
+ * Set the current value of the given property.
  *****************************************************************************/
-static GLenum save_current_contour( GLUtesselator *tobj )
+void GLAPIENTRY gluTessProperty( GLUtesselator *tobj, GLenum which,
+                                GLdouble value )
 {
-    tess_contour_t     *current = tobj->current_contour;
-    tess_vertex_t      *vertex;
-    GLuint                     i;
-
-    if ( current == NULL ) { return GLU_ERROR; }
-
-    if ( tobj->contours == NULL )
-    {
-       tobj->contours = tobj->last_contour = current;
-       current->next = current->previous = NULL;
-    }
-    else
+    switch ( which )
     {
-       current->previous = tobj->last_contour;
+    case GLU_TESS_BOUNDARY_ONLY:
+       tobj->boundary_only = (GLboolean) value;
+       break;
 
-       tobj->last_contour->next = current;
-       tobj->last_contour = current;
+    case GLU_TESS_TOLERANCE:
+       MSG( 15, "   gluTessProperty( tobj:%p ) tolerance: %0.9f\n", tobj, value );
+       tobj->tolerance = value;
+       break;
 
-       current->next = NULL;
-    }
+    case GLU_TESS_WINDING_RULE:
+       tobj->winding_rule = (GLenum) value;
+       break;
 
-    for ( vertex = current->vertices, i = 0;
-         i < current->vertex_count; vertex = vertex->next, i++ )
-    {
-       vertex->shadow_vertex = NULL;
-       vertex->edge_flag = GL_TRUE;
+    default:
+       MSG( 1, "   gluTessProperty( tobj:%p which:%d ) invalid enum\n", tobj, which );
+       tobj->error = GLU_INVALID_ENUM;
+       break;
     }
-
-    current->type = GLU_UNKNOWN;
-
-    tobj->contour_count++;
-    tobj->current_contour = NULL;
-
-    return GLU_NO_ERROR;
 }
 
+
 /*****************************************************************************
- * delete_contour
+ * gluGetTessProperty
  *
- * Delete the given contour and set the pointer to NULL.
+ * Return the current value of the given property.
  *****************************************************************************/
-void delete_contour( tess_contour_t **contour )
+void GLAPIENTRY gluGetTessProperty( GLUtesselator *tobj, GLenum which,
+                                   GLdouble *value )
 {
-    tess_vertex_t      *vertex, *next;
-    GLuint             i;
+    switch ( which )
+    {
+    case GLU_TESS_BOUNDARY_ONLY:
+       *value = tobj->boundary_only;
+       break;
 
-    if ( *contour == NULL ) { return; }
+    case GLU_TESS_TOLERANCE:
+       *value = tobj->tolerance;
+       break;
 
-    vertex = (*contour)->vertices;
+    case GLU_TESS_WINDING_RULE:
+       *value = tobj->winding_rule;
+       break;
 
-    for ( i = 0 ; i < (*contour)->vertex_count ; i++ )
-    {
-       next = vertex->next;
-       free( vertex );
-       vertex = next;
+    default:
+       MSG( 1, "   gluGetTessProperty( tobj:%p which:%d ) invalid enum\n", tobj, which );
+       tobj->error = GLU_INVALID_ENUM;
+       break;
     }
-
-    free( *contour );
-    *contour = NULL;
 }
 
+
 /*****************************************************************************
- * delete_all_contours
+ * gluTessNormal
+ *
+ * Set the current tessellation normal.
  *****************************************************************************/
-static void delete_all_contours( GLUtesselator *tobj )
+void GLAPIENTRY gluTessNormal( GLUtesselator *tobj, GLdouble x,
+                              GLdouble y, GLdouble z )
 {
-    tess_contour_t     *current, *next_contour;
-    GLuint             i;
-
-    if ( tobj->current_contour != NULL ) {
-       delete_contour( &tobj->current_contour );
-    }
-
-    for ( current = tobj->contours, i = 0 ; i < tobj->contour_count ; i++ )
-    {
-       tess_vertex_t   *vertex = current->vertices, *next_vertex;
-       GLuint          j;
-
-       for ( j = 0 ; j < current->vertex_count ; j ++ )
-       {
-           next_vertex = vertex->next;
-           free( vertex );
-           vertex = next_vertex;
-       }
-       next_contour = current->next;
-
-       free( current );
-       current = next_contour;
-    }
-
-    tobj->contour_count = tobj->vertex_count = 0;
-    tobj->contours = tobj->last_contour = NULL;
-
-    CLEAR_BBOX_2DV( tobj->mins, tobj->maxs );
+    MSG( 15, "   gluTessNormal( tobj:%p n:(%.2f,%.2f,%.2f) )\n", tobj, x, y, z );
 
-    ZERO_3V( tobj->plane.normal );
-    tobj->plane.dist = 0.0;
+    ASSIGN_3V( tobj->plane.normal, x, y, z );
 }
 
 
+
 /*****************************************************************************
- * tess_msg
+ *
+ *                     OBSOLETE TESSELLATION FUNCTIONS
+ *
  *****************************************************************************/
-INLINE void tess_msg( int 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 );
-    }
+void GLAPIENTRY gluBeginPolygon( GLUtesselator *tobj )
+{
+    gluTessBeginPolygon( tobj, NULL );
+    gluTessBeginContour( tobj );
+}
 
-    va_end( ap );
-#endif
+void GLAPIENTRY gluNextContour( GLUtesselator *tobj, GLenum type )
+{
+    gluTessEndContour( tobj );
+    gluTessBeginContour( tobj );
 }
 
-INLINE void tess_info( char *file, char *line )
+void GLAPIENTRY gluEndPolygon( GLUtesselator *tobj )
 {
-#ifdef DEBUG
-    fprintf( DBG_STREAM, "%9.9s:%d:\t ", file, line );
-#endif
+    gluTessEndContour( tobj );
+    gluTessEndPolygon( tobj );
 }
index 504ebd0872f1a6110a9497d086a25b3b6789989f..556818307353fb493ba11f86ab8ee5b749984a89 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: tess.h,v 1.15 1999/11/05 20:37:14 gareth Exp $ */
+/* $Id: tess.h,v 1.16 1999/12/06 09:39:34 gareth Exp $ */
 
 /*
  * Mesa 3-D graphics library
@@ -56,33 +56,39 @@ extern "C" {
 struct GLUtesselator
 {
     tess_callbacks_t   callbacks;
-    GLboolean          boundary_only;
     GLenum             winding_rule;
+    GLboolean          boundary_only;
     GLdouble           tolerance;
-    tess_plane_t       plane;
-    GLuint             contour_count;
+    GLenum             orientation;
+    void               *data;
+    GLint              num_contours;
     tess_contour_t     *contours, *last_contour;
     tess_contour_t     *current_contour;
     GLdouble           mins[2], maxs[2];
-    GLuint             vertex_count;
+    GLint              num_vertices;
     tess_vertex_t      **sorted_vertices;
 #if 0
     tess_grid_t                *grid;                  /* Not currently used... */
 #endif
     heap_t             *ears;
-    hashtable_t                *cvc_lists;
-    void               *data;
     GLboolean          edge_flag;
     GLuint             label;
+    tess_plane_t       plane;
     GLenum             error;
 };
 
 
 /*****************************************************************************
- * Tessellation error handler:
+ * Common tessellation functions:
  *****************************************************************************/
 extern void tess_error_callback( GLUtesselator *, GLenum );
 
+extern GLdouble twice_contour_area( tess_contour_t *contour );
+extern void reverse_contour( tess_contour_t *contour );
+extern void delete_contour( tess_contour_t **contour );
+
+extern void contour_dump( tess_contour_t *contour );
+
 
 /*****************************************************************************
  * Debugging output:
@@ -115,8 +121,8 @@ extern      int     tess_dbg_level;
 #define MSG            tess_msg
 #endif /* DEBUG */
 
-extern INLINE void tess_msg( int level, char *format, ... );
-extern INLINE void tess_info( char *file, char *line );
+extern INLINE void tess_msg( GLint level, char *format, ... );
+extern INLINE void tess_info( char *file, GLint line );
 
 #ifdef __cplusplus
 }