1 /*
2  * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
3  * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice including the dates of first publication and
13  * either this permission notice or a reference to
14  * http://oss.sgi.com/projects/FreeB/
15  * shall be included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
22  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  *
25  * Except as contained in this notice, the name of Silicon Graphics, Inc.
26  * shall not be used in advertising or otherwise to promote the sale, use or
27  * other dealings in this Software without prior written authorization from
28  * Silicon Graphics, Inc.
29  */
30 /*
31 ** Author: Eric Veach, July 1994.
32 **
33 */
34 
35 #include <stddef.h>
36 #include <assert.h>
37 #include <setjmp.h>
38 #include "memalloc.h"
39 #include "tess.h"
40 #include "mesh.h"
41 #include "normal.h"
42 #include "sweep.h"
43 #include "tessmono.h"
44 #include "render.h"
45 
46 #define GLU_TESS_DEFAULT_TOLERANCE 0.0
47 #define GLU_TESS_MESH		100112	/* void (*)(GLUmesh *mesh)	    */
48 
49 #ifndef TRUE
50 #define TRUE 1
51 #endif
52 #ifndef FALSE
53 #define FALSE 0
54 #endif
55 
noBegin(GLenum type)56 /*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {}
noEdgeFlag(GLboolean boundaryEdge)57 /*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {}
noVertex(void * data)58 /*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {}
noEnd(void)59 /*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {}
noError(GLenum errnum)60 /*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {}
noCombine(GLdouble coords[3],void * data[4],GLfloat weight[4],void ** dataOut)61 /*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4],
62 				    GLfloat weight[4], void **dataOut ) {}
noMesh(GLUmesh * mesh)63 /*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {}
64 
65 
__gl_noBeginData(GLenum type,void * polygonData)66 /*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type,
67 					     void *polygonData ) {}
__gl_noEdgeFlagData(GLboolean boundaryEdge,void * polygonData)68 /*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge,
69 				       void *polygonData ) {}
__gl_noVertexData(void * data,void * polygonData)70 /*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data,
71 					      void *polygonData ) {}
__gl_noEndData(void * polygonData)72 /*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {}
__gl_noErrorData(GLenum errnum,void * polygonData)73 /*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum,
74 					     void *polygonData ) {}
__gl_noCombineData(GLdouble coords[3],void * data[4],GLfloat weight[4],void ** outData,void * polygonData)75 /*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3],
76 					       void *data[4],
77 					       GLfloat weight[4],
78 					       void **outData,
79 					       void *polygonData ) {}
80 
81 /* Half-edges are allocated in pairs (see mesh.c) */
82 typedef struct { GLUhalfEdge e, eSym; } EdgePair;
83 
84 #undef	MAX
85 #define MAX(a,b)	((a) > (b) ? (a) : (b))
86 #define MAX_FAST_ALLOC	(MAX(sizeof(EdgePair), \
87                          MAX(sizeof(GLUvertex),sizeof(GLUface))))
88 
89 
90 GLUtesselator * GLAPIENTRY
gluNewTess(void)91 gluNewTess( void )
92 {
93   GLUtesselator *tess;
94 
95   /* Only initialize fields which can be changed by the api.  Other fields
96    * are initialized where they are used.
97    */
98 
99   if (memInit( MAX_FAST_ALLOC ) == 0) {
100      return 0;			/* out of memory */
101   }
102   tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator ));
103   if (tess == NULL) {
104      return 0;			/* out of memory */
105   }
106 
107   tess->state = T_DORMANT;
108 
109   tess->normal[0] = 0;
110   tess->normal[1] = 0;
111   tess->normal[2] = 0;
112 
113   tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE;
114   tess->windingRule = GLU_TESS_WINDING_ODD;
115   tess->flagBoundary = FALSE;
116   tess->boundaryOnly = FALSE;
117 
118   tess->callBegin = &noBegin;
119   tess->callEdgeFlag = &noEdgeFlag;
120   tess->callVertex = &noVertex;
121   tess->callEnd = &noEnd;
122 
123   tess->callError = &noError;
124   tess->callCombine = &noCombine;
125   tess->callMesh = &noMesh;
126 
127   tess->callBeginData= &__gl_noBeginData;
128   tess->callEdgeFlagData= &__gl_noEdgeFlagData;
129   tess->callVertexData= &__gl_noVertexData;
130   tess->callEndData= &__gl_noEndData;
131   tess->callErrorData= &__gl_noErrorData;
132   tess->callCombineData= &__gl_noCombineData;
133 
134   tess->polygonData= NULL;
135 
136   return tess;
137 }
138 
MakeDormant(GLUtesselator * tess)139 static void MakeDormant( GLUtesselator *tess )
140 {
141   /* Return the tessellator to its original dormant state. */
142 
143   if( tess->mesh != NULL ) {
144     __gl_meshDeleteMesh( tess->mesh );
145   }
146   tess->state = T_DORMANT;
147   tess->lastEdge = NULL;
148   tess->mesh = NULL;
149 }
150 
151 #define RequireState( tess, s )   if( tess->state != s ) GotoState(tess,s)
152 
GotoState(GLUtesselator * tess,enum TessState newState)153 static void GotoState( GLUtesselator *tess, enum TessState newState )
154 {
155   while( tess->state != newState ) {
156     /* We change the current state one level at a time, to get to
157      * the desired state.
158      */
159     if( tess->state < newState ) {
160       switch( tess->state ) {
161       case T_DORMANT:
162 	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON );
163 	gluTessBeginPolygon( tess, NULL );
164 	break;
165       case T_IN_POLYGON:
166 	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR );
167 	gluTessBeginContour( tess );
168 	break;
169       default:
170 	 ;
171       }
172     } else {
173       switch( tess->state ) {
174       case T_IN_CONTOUR:
175 	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR );
176 	gluTessEndContour( tess );
177 	break;
178       case T_IN_POLYGON:
179 	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON );
180 	/* gluTessEndPolygon( tess ) is too much work! */
181 	MakeDormant( tess );
182 	break;
183       default:
184 	 ;
185       }
186     }
187   }
188 }
189 
190 
191 void GLAPIENTRY
gluDeleteTess(GLUtesselator * tess)192 gluDeleteTess( GLUtesselator *tess )
193 {
194   RequireState( tess, T_DORMANT );
195   memFree( tess );
196 }
197 
198 
199 void GLAPIENTRY
gluTessProperty(GLUtesselator * tess,GLenum which,GLdouble value)200 gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value )
201 {
202   GLenum windingRule;
203 
204   switch( which ) {
205   case GLU_TESS_TOLERANCE:
206     if( value < 0.0 || value > 1.0 ) break;
207     tess->relTolerance = value;
208     return;
209 
210   case GLU_TESS_WINDING_RULE:
211     windingRule = (GLenum) value;
212     if( windingRule != value ) break;	/* not an integer */
213 
214     switch( windingRule ) {
215     case GLU_TESS_WINDING_ODD:
216     case GLU_TESS_WINDING_NONZERO:
217     case GLU_TESS_WINDING_POSITIVE:
218     case GLU_TESS_WINDING_NEGATIVE:
219     case GLU_TESS_WINDING_ABS_GEQ_TWO:
220       tess->windingRule = windingRule;
221       return;
222     default:
223       break;
224     }
225 
226   case GLU_TESS_BOUNDARY_ONLY:
227     tess->boundaryOnly = (value != 0);
228     return;
229 
230   default:
231     CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
232     return;
233   }
234   CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE );
235 }
236 
237 /* Returns tessellator property */
238 void GLAPIENTRY
gluGetTessProperty(GLUtesselator * tess,GLenum which,GLdouble * value)239 gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value )
240 {
241    switch (which) {
242    case GLU_TESS_TOLERANCE:
243       /* tolerance should be in range [0..1] */
244       assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0);
245       *value= tess->relTolerance;
246       break;
247    case GLU_TESS_WINDING_RULE:
248       assert(tess->windingRule == GLU_TESS_WINDING_ODD ||
249 	     tess->windingRule == GLU_TESS_WINDING_NONZERO ||
250 	     tess->windingRule == GLU_TESS_WINDING_POSITIVE ||
251 	     tess->windingRule == GLU_TESS_WINDING_NEGATIVE ||
252 	     tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO);
253       *value= tess->windingRule;
254       break;
255    case GLU_TESS_BOUNDARY_ONLY:
256       assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE);
257       *value= tess->boundaryOnly;
258       break;
259    default:
260       *value= 0.0;
261       CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
262       break;
263    }
264 } /* gluGetTessProperty() */
265 
266 void GLAPIENTRY
gluTessNormal(GLUtesselator * tess,GLdouble x,GLdouble y,GLdouble z)267 gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z )
268 {
269   tess->normal[0] = x;
270   tess->normal[1] = y;
271   tess->normal[2] = z;
272 }
273 
274 void GLAPIENTRY
gluTessCallback(GLUtesselator * tess,GLenum which,_GLUfuncptr fn)275 gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn)
276 {
277   switch( which ) {
278   case GLU_TESS_BEGIN:
279     tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn;
280     return;
281   case GLU_TESS_BEGIN_DATA:
282     tess->callBeginData = (fn == NULL) ?
283 	&__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
284     return;
285   case GLU_TESS_EDGE_FLAG:
286     tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag :
287 					(void (GLAPIENTRY *)(GLboolean)) fn;
288     /* If the client wants boundary edges to be flagged,
289      * we render everything as separate triangles (no strips or fans).
290      */
291     tess->flagBoundary = (fn != NULL);
292     return;
293   case GLU_TESS_EDGE_FLAG_DATA:
294     tess->callEdgeFlagData= (fn == NULL) ?
295 	&__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn;
296     /* If the client wants boundary edges to be flagged,
297      * we render everything as separate triangles (no strips or fans).
298      */
299     tess->flagBoundary = (fn != NULL);
300     return;
301   case GLU_TESS_VERTEX:
302     tess->callVertex = (fn == NULL) ? &noVertex :
303 				      (void (GLAPIENTRY *)(void *)) fn;
304     return;
305   case GLU_TESS_VERTEX_DATA:
306     tess->callVertexData = (fn == NULL) ?
307 	&__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn;
308     return;
309   case GLU_TESS_END:
310     tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn;
311     return;
312   case GLU_TESS_END_DATA:
313     tess->callEndData = (fn == NULL) ? &__gl_noEndData :
314 				       (void (GLAPIENTRY *)(void *)) fn;
315     return;
316   case GLU_TESS_ERROR:
317     tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn;
318     return;
319   case GLU_TESS_ERROR_DATA:
320     tess->callErrorData = (fn == NULL) ?
321 	&__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
322     return;
323   case GLU_TESS_COMBINE:
324     tess->callCombine = (fn == NULL) ? &noCombine :
325 	(void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn;
326     return;
327   case GLU_TESS_COMBINE_DATA:
328     tess->callCombineData = (fn == NULL) ? &__gl_noCombineData :
329 					   (void (GLAPIENTRY *)(GLdouble [3],
330 						     void *[4],
331 						     GLfloat [4],
332 						     void **,
333 						     void *)) fn;
334     return;
335   case GLU_TESS_MESH:
336     tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn;
337     return;
338   default:
339     CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
340     return;
341   }
342 }
343 
AddVertex(GLUtesselator * tess,GLdouble coords[3],void * data)344 static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
345 {
346   GLUhalfEdge *e;
347 
348   e = tess->lastEdge;
349   if( e == NULL ) {
350     /* Make a self-loop (one vertex, one edge). */
351 
352     e = __gl_meshMakeEdge( tess->mesh );
353     if (e == NULL) return 0;
354     if ( !__gl_meshSplice( e, e->Sym ) ) return 0;
355   } else {
356     /* Create a new vertex and edge which immediately follow e
357      * in the ordering around the left face.
358      */
359     if (__gl_meshSplitEdge( e ) == NULL) return 0;
360     e = e->Lnext;
361   }
362 
363   /* The new vertex is now e->Org. */
364   e->Org->data = data;
365   e->Org->coords[0] = coords[0];
366   e->Org->coords[1] = coords[1];
367   e->Org->coords[2] = coords[2];
368 
369   /* The winding of an edge says how the winding number changes as we
370    * cross from the edge''s right face to its left face.  We add the
371    * vertices in such an order that a CCW contour will add +1 to
372    * the winding number of the region inside the contour.
373    */
374   e->winding = 1;
375   e->Sym->winding = -1;
376 
377   tess->lastEdge = e;
378 
379   return 1;
380 }
381 
382 
CacheVertex(GLUtesselator * tess,GLdouble coords[3],void * data)383 static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
384 {
385   CachedVertex *v = &tess->cache[tess->cacheCount];
386 
387   v->data = data;
388   v->coords[0] = coords[0];
389   v->coords[1] = coords[1];
390   v->coords[2] = coords[2];
391   ++tess->cacheCount;
392 }
393 
394 
EmptyCache(GLUtesselator * tess)395 static int EmptyCache( GLUtesselator *tess )
396 {
397   CachedVertex *v = tess->cache;
398   CachedVertex *vLast;
399 
400   tess->mesh = __gl_meshNewMesh();
401   if (tess->mesh == NULL) return 0;
402 
403   for( vLast = v + tess->cacheCount; v < vLast; ++v ) {
404     if ( !AddVertex( tess, v->coords, v->data ) ) return 0;
405   }
406   tess->cacheCount = 0;
407   tess->emptyCache = FALSE;
408 
409   return 1;
410 }
411 
412 
413 void GLAPIENTRY
gluTessVertex(GLUtesselator * tess,GLdouble coords[3],void * data)414 gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
415 {
416   int i, tooLarge = FALSE;
417   GLdouble x, clamped[3];
418 
419   RequireState( tess, T_IN_CONTOUR );
420 
421   if( tess->emptyCache ) {
422     if ( !EmptyCache( tess ) ) {
423        CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
424        return;
425     }
426     tess->lastEdge = NULL;
427   }
428   for( i = 0; i < 3; ++i ) {
429     x = coords[i];
430     if( x < - GLU_TESS_MAX_COORD ) {
431       x = - GLU_TESS_MAX_COORD;
432       tooLarge = TRUE;
433     }
434     if( x > GLU_TESS_MAX_COORD ) {
435       x = GLU_TESS_MAX_COORD;
436       tooLarge = TRUE;
437     }
438     clamped[i] = x;
439   }
440   if( tooLarge ) {
441     CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE );
442   }
443 
444   if( tess->mesh == NULL ) {
445     if( tess->cacheCount < TESS_MAX_CACHE ) {
446       CacheVertex( tess, clamped, data );
447       return;
448     }
449     if ( !EmptyCache( tess ) ) {
450        CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
451        return;
452     }
453   }
454   if ( !AddVertex( tess, clamped, data ) ) {
455        CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
456   }
457 }
458 
459 
460 void GLAPIENTRY
gluTessBeginPolygon(GLUtesselator * tess,void * data)461 gluTessBeginPolygon( GLUtesselator *tess, void *data )
462 {
463   RequireState( tess, T_DORMANT );
464 
465   tess->state = T_IN_POLYGON;
466   tess->cacheCount = 0;
467   tess->emptyCache = FALSE;
468   tess->mesh = NULL;
469 
470   tess->polygonData= data;
471 }
472 
473 
474 void GLAPIENTRY
gluTessBeginContour(GLUtesselator * tess)475 gluTessBeginContour( GLUtesselator *tess )
476 {
477   RequireState( tess, T_IN_POLYGON );
478 
479   tess->state = T_IN_CONTOUR;
480   tess->lastEdge = NULL;
481   if( tess->cacheCount > 0 ) {
482     /* Just set a flag so we don't get confused by empty contours
483      * -- these can be generated accidentally with the obsolete
484      * NextContour() interface.
485      */
486     tess->emptyCache = TRUE;
487   }
488 }
489 
490 
491 void GLAPIENTRY
gluTessEndContour(GLUtesselator * tess)492 gluTessEndContour( GLUtesselator *tess )
493 {
494   RequireState( tess, T_IN_CONTOUR );
495   tess->state = T_IN_POLYGON;
496 }
497 
498 void GLAPIENTRY
gluTessEndPolygon(GLUtesselator * tess)499 gluTessEndPolygon( GLUtesselator *tess )
500 {
501   GLUmesh *mesh;
502 
503   if (setjmp(tess->env) != 0) {
504      /* come back here if out of memory */
505      CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
506      return;
507   }
508 
509   RequireState( tess, T_IN_POLYGON );
510   tess->state = T_DORMANT;
511 
512   if( tess->mesh == NULL ) {
513     if( ! tess->flagBoundary && tess->callMesh == &noMesh ) {
514 
515       /* Try some special code to make the easy cases go quickly
516        * (eg. convex polygons).  This code does NOT handle multiple contours,
517        * intersections, edge flags, and of course it does not generate
518        * an explicit mesh either.
519        */
520       if( __gl_renderCache( tess )) {
521 	tess->polygonData= NULL;
522 	return;
523       }
524     }
525     if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/
526   }
527 
528   /* Determine the polygon normal and project vertices onto the plane
529    * of the polygon.
530    */
531   __gl_projectPolygon( tess );
532 
533   /* __gl_computeInterior( tess ) computes the planar arrangement specified
534    * by the given contours, and further subdivides this arrangement
535    * into regions.  Each region is marked "inside" if it belongs
536    * to the polygon, according to the rule given by tess->windingRule.
537    * Each interior region is guaranteed be monotone.
538    */
539   if ( !__gl_computeInterior( tess ) ) {
540      longjmp(tess->env,1);	/* could've used a label */
541   }
542 
543   mesh = tess->mesh;
544   if( ! tess->fatalError ) {
545     int rc = 1;
546 
547     /* If the user wants only the boundary contours, we throw away all edges
548      * except those which separate the interior from the exterior.
549      * Otherwise we tessellate all the regions marked "inside".
550      */
551     if( tess->boundaryOnly ) {
552       rc = __gl_meshSetWindingNumber( mesh, 1, TRUE );
553     } else {
554       rc = __gl_meshTessellateInterior( mesh );
555     }
556     if (rc == 0) longjmp(tess->env,1);	/* could've used a label */
557 
558     __gl_meshCheckMesh( mesh );
559 
560     if( tess->callBegin != &noBegin || tess->callEnd != &noEnd
561        || tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag
562        || tess->callBeginData != &__gl_noBeginData
563        || tess->callEndData != &__gl_noEndData
564        || tess->callVertexData != &__gl_noVertexData
565        || tess->callEdgeFlagData != &__gl_noEdgeFlagData )
566     {
567       if( tess->boundaryOnly ) {
568 	__gl_renderBoundary( tess, mesh );  /* output boundary contours */
569       } else {
570 	__gl_renderMesh( tess, mesh );	   /* output strips and fans */
571       }
572     }
573     if( tess->callMesh != &noMesh ) {
574 
575       /* Throw away the exterior faces, so that all faces are interior.
576        * This way the user doesn't have to check the "inside" flag,
577        * and we don't need to even reveal its existence.  It also leaves
578        * the freedom for an implementation to not generate the exterior
579        * faces in the first place.
580        */
581       __gl_meshDiscardExterior( mesh );
582       (*tess->callMesh)( mesh );		/* user wants the mesh itself */
583       tess->mesh = NULL;
584       tess->polygonData= NULL;
585       return;
586     }
587   }
588   __gl_meshDeleteMesh( mesh );
589   tess->polygonData= NULL;
590   tess->mesh = NULL;
591 }
592 
593 
594 /*XXXblythe unused function*/
595 #if 0
596 void GLAPIENTRY
597 gluDeleteMesh( GLUmesh *mesh )
598 {
599   __gl_meshDeleteMesh( mesh );
600 }
601 #endif
602 
603 
604 
605 /*******************************************************/
606 
607 /* Obsolete calls -- for backward compatibility */
608 
609 void GLAPIENTRY
gluBeginPolygon(GLUtesselator * tess)610 gluBeginPolygon( GLUtesselator *tess )
611 {
612   gluTessBeginPolygon( tess, NULL );
613   gluTessBeginContour( tess );
614 }
615 
616 
617 /*ARGSUSED*/
618 void GLAPIENTRY
gluNextContour(GLUtesselator * tess,GLenum type)619 gluNextContour( GLUtesselator *tess, GLenum type )
620 {
621   gluTessEndContour( tess );
622   gluTessBeginContour( tess );
623 }
624 
625 
626 void GLAPIENTRY
gluEndPolygon(GLUtesselator * tess)627 gluEndPolygon( GLUtesselator *tess )
628 {
629   gluTessEndContour( tess );
630   gluTessEndPolygon( tess );
631 }
632