2
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
3
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
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:
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.
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
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.
31
** Author: Eric Veach, July 1994.
33
* Important: Fabien Chereau - September 2009
34
* Modifs for the use of Stellarium
50
#define GLUES_TESS_DEFAULT_TOLERANCE 0.0f
51
#define GLUES_TESS_MESH 100112 /* void (*)(GLUESmesh *mesh) */
57
/*ARGSUSED*/ static void noBegin(GLenum type) {}
58
/*ARGSUSED*/ static void noEdgeFlag(GLboolean boundaryEdge ) {}
59
/*ARGSUSED*/ static void noVertex(void* data) {}
60
/*ARGSUSED*/ static void noEnd(void) {}
61
/*ARGSUSED*/ static void noError(GLenum errnum) {}
62
/*ARGSUSED*/ static void noCombine(double coords[3], void *data[4],
63
GLfloat weight[4], void **dataOut) {}
64
/*ARGSUSED*/ static void noMesh(GLUESmesh* mesh) {}
66
/*ARGSUSED*/ void __gl_noBeginData(GLenum type, void* polygonData) {}
67
/*ARGSUSED*/ void __gl_noEdgeFlagData(GLboolean boundaryEdge, void* polygonData) {}
68
/*ARGSUSED*/ void __gl_noVertexData(void* data, void* polygonData) {}
69
/*ARGSUSED*/ void __gl_noEndData(void* polygonData) {}
70
/*ARGSUSED*/ void __gl_noErrorData( GLenum errnum, void* polygonData) {}
71
/*ARGSUSED*/ void __gl_noCombineData(double coords[3], void* data[4],
72
GLfloat weight[4], void** outData,
75
/* Half-edges are allocated in pairs (see mesh.c) */
76
typedef struct {GLUEShalfEdge e, eSym;} EdgePair;
79
#define MAX(a, b) ((a)>(b) ? (a): (b))
80
#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \
81
MAX(sizeof(GLUESvertex), sizeof(GLUESface))))
83
GLUEStesselator* gluesNewTess(void)
85
GLUEStesselator* tess;
87
/* Only initialize fields which can be changed by the api. Other fields
88
* are initialized where they are used.
91
if (memInit(MAX_FAST_ALLOC)==0)
93
return 0; /* out of memory */
95
tess=(GLUEStesselator*)memAlloc(sizeof(GLUEStesselator));
98
return 0; /* out of memory */
101
tess->state=T_DORMANT;
107
tess->relTolerance=GLUES_TESS_DEFAULT_TOLERANCE;
108
tess->windingRule=GLUES_TESS_WINDING_ODD;
109
tess->flagBoundary=FALSE;
110
tess->boundaryOnly=FALSE;
112
tess->callBegin=&noBegin;
113
tess->callEdgeFlag=&noEdgeFlag;
114
tess->callVertex=&noVertex;
115
tess->callEnd=&noEnd;
117
tess->callError=&noError;
118
tess->callCombine=&noCombine;
119
tess->callMesh=&noMesh;
121
tess->callBeginData=&__gl_noBeginData;
122
tess->callEdgeFlagData=&__gl_noEdgeFlagData;
123
tess->callVertexData=&__gl_noVertexData;
124
tess->callEndData=&__gl_noEndData;
125
tess->callErrorData=&__gl_noErrorData;
126
tess->callCombineData=&__gl_noCombineData;
128
tess->polygonData=NULL;
133
static void MakeDormant( GLUEStesselator *tess )
135
/* Return the tessellator to its original dormant state. */
136
if (tess->mesh!=NULL)
138
__gl_meshDeleteMesh(tess->mesh);
140
tess->state=T_DORMANT;
145
#define RequireState(tess, s) if (tess->state!=s) { GotoState(tess, s); }
147
static void GotoState(GLUEStesselator* tess, enum TessState newState)
149
while (tess->state!=newState)
151
/* We change the current state one level at a time, to get to
154
if (tess->state<newState)
159
CALL_ERROR_OR_ERROR_DATA(GLUES_TESS_MISSING_BEGIN_POLYGON);
160
gluesTessBeginPolygon(tess, NULL);
163
CALL_ERROR_OR_ERROR_DATA(GLUES_TESS_MISSING_BEGIN_CONTOUR);
164
gluesTessBeginContour(tess);
175
CALL_ERROR_OR_ERROR_DATA( GLUES_TESS_MISSING_END_CONTOUR );
176
gluesTessEndContour(tess);
179
CALL_ERROR_OR_ERROR_DATA(GLUES_TESS_MISSING_END_POLYGON);
180
/* gluesTessEndPolygon(tess) is too much work! */
190
void gluesDeleteTess(GLUEStesselator* tess)
192
RequireState(tess, T_DORMANT);
196
void gluesTessProperty(GLUEStesselator* tess, GLenum which, double value)
202
case GLUES_TESS_TOLERANCE:
203
if (value<0.0f || value>1.0f)
207
tess->relTolerance = value;
209
case GLUES_TESS_WINDING_RULE:
210
windingRule=(GLenum)value;
211
if (windingRule!=value)
213
break; /* not an integer */
218
case GLUES_TESS_WINDING_ODD:
219
case GLUES_TESS_WINDING_NONZERO:
220
case GLUES_TESS_WINDING_POSITIVE:
221
case GLUES_TESS_WINDING_NEGATIVE:
222
case GLUES_TESS_WINDING_ABS_GEQ_TWO:
223
tess->windingRule=windingRule;
229
case GLUES_TESS_BOUNDARY_ONLY:
230
tess->boundaryOnly=(value!=0);
233
CALL_ERROR_OR_ERROR_DATA(GLUES_INVALID_ENUM);
237
CALL_ERROR_OR_ERROR_DATA(GLUES_INVALID_VALUE);
240
/* Returns tessellator property */
241
void gluesGetTessProperty(GLUEStesselator* tess, GLenum which, double* value)
245
case GLUES_TESS_TOLERANCE:
246
/* tolerance should be in range [0..1] */
247
assert(0.0f<=tess->relTolerance && tess->relTolerance<=1.0f);
248
*value=tess->relTolerance;
250
case GLUES_TESS_WINDING_RULE:
251
assert(tess->windingRule==GLUES_TESS_WINDING_ODD ||
252
tess->windingRule==GLUES_TESS_WINDING_NONZERO ||
253
tess->windingRule==GLUES_TESS_WINDING_POSITIVE ||
254
tess->windingRule==GLUES_TESS_WINDING_NEGATIVE ||
255
tess->windingRule==GLUES_TESS_WINDING_ABS_GEQ_TWO);
256
*value=(GLfloat)tess->windingRule;
258
case GLUES_TESS_BOUNDARY_ONLY:
259
assert(tess->boundaryOnly==TRUE || tess->boundaryOnly==FALSE);
260
*value=tess->boundaryOnly;
264
CALL_ERROR_OR_ERROR_DATA(GLUES_INVALID_ENUM);
269
void gluesTessNormal(GLUEStesselator* tess, double x, double y, double z)
276
void gluesTessCallback(GLUEStesselator* tess, GLenum which, _GLUESfuncptr fn)
280
case GLUES_TESS_BEGIN:
281
tess->callBegin=(fn==NULL) ? &noBegin: (void (*)(GLenum))fn;
283
case GLUES_TESS_BEGIN_DATA:
284
tess->callBeginData=(fn==NULL) ?
285
&__gl_noBeginData: (void (*)(GLenum, void*))fn;
287
case GLUES_TESS_EDGE_FLAG:
288
tess->callEdgeFlag=(fn==NULL) ? &noEdgeFlag: (void (*)(GLboolean))fn;
289
/* If the client wants boundary edges to be flagged,
290
* we render everything as separate triangles (no strips or fans).
292
tess->flagBoundary=(fn!=NULL);
294
case GLUES_TESS_EDGE_FLAG_DATA:
295
tess->callEdgeFlagData=(fn==NULL) ?
296
&__gl_noEdgeFlagData: (void (*)(GLboolean, void*))fn;
297
/* If the client wants boundary edges to be flagged,
298
* we render everything as separate triangles (no strips or fans).
300
tess->flagBoundary=(fn!=NULL);
302
case GLUES_TESS_VERTEX:
303
tess->callVertex=(fn==NULL) ? &noVertex: (void (*)(void*))fn;
305
case GLUES_TESS_VERTEX_DATA:
306
tess->callVertexData=(fn==NULL) ?
307
&__gl_noVertexData: (void (*)(void*, void*))fn;
310
tess->callEnd=(fn==NULL) ? &noEnd: (void (*)(void))fn;
312
case GLUES_TESS_END_DATA:
313
tess->callEndData=(fn==NULL) ? &__gl_noEndData: (void (*)(void*))fn;
315
case GLUES_TESS_ERROR:
316
tess->callError=(fn==NULL) ? &noError: (void (*)(GLenum))fn;
318
case GLUES_TESS_ERROR_DATA:
319
tess->callErrorData=(fn==NULL) ? &__gl_noErrorData: (void (*)(GLenum, void*))fn;
321
case GLUES_TESS_COMBINE:
322
tess->callCombine=(fn==NULL) ? &noCombine:
323
(void (*)(double[3], void*[4], GLfloat[4], void**))fn;
325
case GLUES_TESS_COMBINE_DATA:
326
tess->callCombineData=(fn==NULL) ? &__gl_noCombineData:
327
(void (*)(double [3], void*[4], GLfloat[4], void**, void*))fn;
329
case GLUES_TESS_MESH:
330
tess->callMesh=(fn==NULL) ? &noMesh: (void (*)(GLUESmesh*))fn;
333
CALL_ERROR_OR_ERROR_DATA( GLUES_INVALID_ENUM );
338
static int AddVertex(GLUEStesselator* tess, double coords[3], void* data)
340
GLUEShalfEdge* e=NULL;
345
/* Make a self-loop (one vertex, one edge). */
346
e=__gl_meshMakeEdge(tess->mesh);
351
if (!__gl_meshSplice(e, e->Sym))
358
/* Create a new vertex and edge which immediately follow e
359
* in the ordering around the left face.
361
if (__gl_meshSplitEdge(e)==NULL)
368
/* The new vertex is now e->Org. */
370
e->Org->coords[0]=coords[0];
371
e->Org->coords[1]=coords[1];
372
e->Org->coords[2]=coords[2];
374
/* The winding of an edge says how the winding number changes as we
375
* cross from the edge''s right face to its left face. We add the
376
* vertices in such an order that a CCW contour will add +1 to
377
* the winding number of the region inside the contour.
387
static void CacheVertex(GLUEStesselator* tess, double coords[3], void* data)
389
CachedVertex* v=&tess->cache[tess->cacheCount];
392
v->coords[0]=coords[0];
393
v->coords[1]=coords[1];
394
v->coords[2]=coords[2];
398
static int EmptyCache(GLUEStesselator* tess)
400
CachedVertex* v=tess->cache;
403
tess->mesh=__gl_meshNewMesh();
404
if (tess->mesh==NULL)
409
for(vLast=v+tess->cacheCount; v<vLast; ++v)
411
if (!AddVertex(tess, v->coords, v->data))
417
tess->emptyCache=FALSE;
423
void gluesTessVertex(GLUEStesselator* tess, double coords[3], void* data)
427
double x, clamped[3];
429
RequireState(tess, T_IN_CONTOUR);
431
if (tess->emptyCache)
433
if (!EmptyCache(tess))
435
CALL_ERROR_OR_ERROR_DATA( GLUES_OUT_OF_MEMORY );
444
if (x<-GLUES_TESS_MAX_COORD)
446
x=-GLUES_TESS_MAX_COORD;
449
if (x>GLUES_TESS_MAX_COORD)
451
x=GLUES_TESS_MAX_COORD;
458
CALL_ERROR_OR_ERROR_DATA(GLUES_TESS_COORD_TOO_LARGE);
461
if (tess->mesh==NULL)
463
if (tess->cacheCount<TESS_MAX_CACHE)
465
CacheVertex(tess, clamped, data);
468
if (!EmptyCache(tess))
470
CALL_ERROR_OR_ERROR_DATA(GLUES_OUT_OF_MEMORY);
475
if (!AddVertex(tess, clamped, data))
477
CALL_ERROR_OR_ERROR_DATA(GLUES_OUT_OF_MEMORY);
481
void gluesTessBeginPolygon(GLUEStesselator* tess, void* data)
483
RequireState(tess, T_DORMANT);
485
tess->state=T_IN_POLYGON;
487
tess->emptyCache=FALSE;
490
tess->polygonData=data;
493
void gluesTessBeginContour(GLUEStesselator* tess)
495
RequireState(tess, T_IN_POLYGON);
497
tess->state=T_IN_CONTOUR;
499
if (tess->cacheCount>0)
501
/* Just set a flag so we don't get confused by empty contours
502
* -- these can be generated accidentally with the obsolete
503
* NextContour() interface.
505
tess->emptyCache=TRUE;
509
void gluesTessEndContour(GLUEStesselator* tess)
511
RequireState(tess, T_IN_CONTOUR);
512
tess->state=T_IN_POLYGON;
515
void gluesTessEndPolygon(GLUEStesselator* tess)
519
if (setjmp(tess->env)!=0)
521
/* come back here if out of memory */
522
CALL_ERROR_OR_ERROR_DATA(GLUES_OUT_OF_MEMORY);
526
RequireState(tess, T_IN_POLYGON);
527
tess->state=T_DORMANT;
529
if (tess->mesh==NULL)
531
if (!tess->flagBoundary && tess->callMesh==&noMesh)
533
/* Try some special code to make the easy cases go quickly
534
* (eg. convex polygons). This code does NOT handle multiple contours,
535
* intersections, edge flags, and of course it does not generate
536
* an explicit mesh either.
538
if (__gl_renderCache(tess))
540
tess->polygonData= NULL;
544
if (!EmptyCache(tess))
546
longjmp(tess->env, 1); /* could've used a label */
550
/* Determine the polygon normal and project vertices onto the plane
553
__gl_projectPolygon(tess);
555
/* __gl_computeInterior( tess ) computes the planar arrangement specified
556
* by the given contours, and further subdivides this arrangement
557
* into regions. Each region is marked "inside" if it belongs
558
* to the polygon, according to the rule given by tess->windingRule.
559
* Each interior region is guaranteed be monotone.
561
if (!__gl_computeInterior(tess))
563
longjmp(tess->env, 1); /* could've used a label */
567
if (!tess->fatalError)
571
/* If the user wants only the boundary contours, we throw away all edges
572
* except those which separate the interior from the exterior.
573
* Otherwise we tessellate all the regions marked "inside".
575
if (tess->boundaryOnly)
577
rc=__gl_meshSetWindingNumber(mesh, 1, TRUE);
581
rc=__gl_meshTessellateInterior(mesh);
585
longjmp(tess->env,1); /* could've used a label */
588
__gl_meshCheckMesh(mesh);
590
if (tess->callBegin!=&noBegin || tess->callEnd!=&noEnd ||
591
tess->callVertex!=&noVertex || tess->callEdgeFlag!=&noEdgeFlag ||
592
tess->callBeginData!=&__gl_noBeginData || tess->callEndData!=&__gl_noEndData ||
593
tess->callVertexData!=&__gl_noVertexData || tess->callEdgeFlagData!=&__gl_noEdgeFlagData)
595
if (tess->boundaryOnly)
597
__gl_renderBoundary(tess, mesh); /* output boundary contours */
601
__gl_renderMesh(tess, mesh); /* output strips and fans */
605
if (tess->callMesh!=&noMesh)
607
/* Throw away the exterior faces, so that all faces are interior.
608
* This way the user doesn't have to check the "inside" flag,
609
* and we don't need to even reveal its existence. It also leaves
610
* the freedom for an implementation to not generate the exterior
611
* faces in the first place.
613
__gl_meshDiscardExterior(mesh);
614
(*tess->callMesh)(mesh); /* user wants the mesh itself */
616
tess->polygonData= NULL;
620
__gl_meshDeleteMesh(mesh);
621
tess->polygonData=NULL;
625
/*******************************************************/
627
/* Obsolete calls -- for backward compatibility */
628
void gluesBeginPolygon(GLUEStesselator* tess)
630
gluesTessBeginPolygon(tess, NULL);
631
gluesTessBeginContour(tess);
635
void gluesNextContour(GLUEStesselator* tess, GLenum type)
637
gluesTessEndContour(tess);
638
gluesTessBeginContour(tess);
641
void gluesEndPolygon(GLUEStesselator* tess)
643
gluesTessEndContour(tess);
644
gluesTessEndPolygon(tess);