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