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