7365c3aa77
This splits out the cogl_path_ api into a separate cogl-path sub-library like cogl-pango and cogl-gst. This enables developers to build Cogl with this sub-library disabled if they don't need it which can be useful when its important to keep the size of an application and its dependencies down to a minimum. The functions cogl_framebuffer_{fill,stroke}_path have been renamed to cogl_path_{fill,stroke}. There were a few places in core cogl and cogl-gst that referenced the CoglPath api and these have been decoupled by using the CoglPrimitive api instead. In the case of cogl_framebuffer_push_path_clip() the core clip stack no longer accepts path clips directly but it's now possible to get a CoglPrimitive for the fill of a path and so the implementation of cogl_framebuffer_push_path_clip() now lives in cogl-path and works as a shim that first gets a CoglPrimitive and uses cogl_framebuffer_push_primitive_clip instead. We may want to consider renaming cogl_framebuffer_push_path_clip to put it in the cogl_path_ namespace. Reviewed-by: Neil Roberts <neil@linux.intel.com> (cherry picked from commit 8aadfd829239534fb4ec8255cdea813d698c5a3f) So as to avoid breaking the 1.x API or even the ABI since we are quite late in the 1.16 development cycle the patch was modified to build cogl-path as a noinst_LTLIBRARY before building cogl and link the code directly into libcogl.so as it was previously. This way we can wait until the start of the 1.18 cycle before splitting the code into a separate libcogl-path.so. This also adds shims for cogl_framebuffer_fill/stroke_path() to avoid breaking the 1.x API/ABI.
633 lines
17 KiB
C
633 lines
17 KiB
C
/*
|
|
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
|
|
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice including the dates of first publication and
|
|
* either this permission notice or a reference to
|
|
* http://oss.sgi.com/projects/FreeB/
|
|
* shall be included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of Silicon Graphics, Inc.
|
|
* shall not be used in advertising or otherwise to promote the sale, use or
|
|
* other dealings in this Software without prior written authorization from
|
|
* Silicon Graphics, Inc.
|
|
*/
|
|
/*
|
|
** Author: Eric Veach, July 1994.
|
|
**
|
|
*/
|
|
|
|
#include "gluos.h"
|
|
#include <stddef.h>
|
|
#include <assert.h>
|
|
#include <setjmp.h>
|
|
#include "memalloc.h"
|
|
#include "tess.h"
|
|
#include "mesh.h"
|
|
#include "normal.h"
|
|
#include "sweep.h"
|
|
#include "tessmono.h"
|
|
#include "render.h"
|
|
|
|
#define GLU_TESS_DEFAULT_TOLERANCE 0.0
|
|
#define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {}
|
|
/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {}
|
|
/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {}
|
|
/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {}
|
|
/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {}
|
|
/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4],
|
|
GLfloat weight[4], void **dataOut ) {}
|
|
/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {}
|
|
|
|
|
|
/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type,
|
|
void *polygonData ) {}
|
|
/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge,
|
|
void *polygonData ) {}
|
|
/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data,
|
|
void *polygonData ) {}
|
|
/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {}
|
|
/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum,
|
|
void *polygonData ) {}
|
|
/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3],
|
|
void *data[4],
|
|
GLfloat weight[4],
|
|
void **outData,
|
|
void *polygonData ) {}
|
|
|
|
/* Half-edges are allocated in pairs (see mesh.c) */
|
|
typedef struct { GLUhalfEdge e, eSym; } EdgePair;
|
|
|
|
#undef MAX
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \
|
|
MAX(sizeof(GLUvertex),sizeof(GLUface))))
|
|
|
|
|
|
GLUtesselator * GLAPIENTRY
|
|
gluNewTess( void )
|
|
{
|
|
GLUtesselator *tess;
|
|
|
|
/* Only initialize fields which can be changed by the api. Other fields
|
|
* are initialized where they are used.
|
|
*/
|
|
|
|
if (memInit( MAX_FAST_ALLOC ) == 0) {
|
|
return 0; /* out of memory */
|
|
}
|
|
tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator ));
|
|
if (tess == NULL) {
|
|
return 0; /* out of memory */
|
|
}
|
|
|
|
tess->state = T_DORMANT;
|
|
|
|
tess->normal[0] = 0;
|
|
tess->normal[1] = 0;
|
|
tess->normal[2] = 0;
|
|
|
|
tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE;
|
|
tess->windingRule = GLU_TESS_WINDING_ODD;
|
|
tess->flagBoundary = FALSE;
|
|
tess->boundaryOnly = FALSE;
|
|
|
|
tess->callBegin = &noBegin;
|
|
tess->callEdgeFlag = &noEdgeFlag;
|
|
tess->callVertex = &noVertex;
|
|
tess->callEnd = &noEnd;
|
|
|
|
tess->callError = &noError;
|
|
tess->callCombine = &noCombine;
|
|
tess->callMesh = &noMesh;
|
|
|
|
tess->callBeginData= &__gl_noBeginData;
|
|
tess->callEdgeFlagData= &__gl_noEdgeFlagData;
|
|
tess->callVertexData= &__gl_noVertexData;
|
|
tess->callEndData= &__gl_noEndData;
|
|
tess->callErrorData= &__gl_noErrorData;
|
|
tess->callCombineData= &__gl_noCombineData;
|
|
|
|
tess->polygonData= NULL;
|
|
|
|
return tess;
|
|
}
|
|
|
|
static void MakeDormant( GLUtesselator *tess )
|
|
{
|
|
/* Return the tessellator to its original dormant state. */
|
|
|
|
if( tess->mesh != NULL ) {
|
|
__gl_meshDeleteMesh( tess->mesh );
|
|
}
|
|
tess->state = T_DORMANT;
|
|
tess->lastEdge = NULL;
|
|
tess->mesh = NULL;
|
|
}
|
|
|
|
#define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s)
|
|
|
|
static void GotoState( GLUtesselator *tess, enum TessState newState )
|
|
{
|
|
while( tess->state != newState ) {
|
|
/* We change the current state one level at a time, to get to
|
|
* the desired state.
|
|
*/
|
|
if( tess->state < newState ) {
|
|
switch( tess->state ) {
|
|
case T_DORMANT:
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON );
|
|
gluTessBeginPolygon( tess, NULL );
|
|
break;
|
|
case T_IN_POLYGON:
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR );
|
|
gluTessBeginContour( tess );
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
} else {
|
|
switch( tess->state ) {
|
|
case T_IN_CONTOUR:
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR );
|
|
gluTessEndContour( tess );
|
|
break;
|
|
case T_IN_POLYGON:
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON );
|
|
/* gluTessEndPolygon( tess ) is too much work! */
|
|
MakeDormant( tess );
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void GLAPIENTRY
|
|
gluDeleteTess( GLUtesselator *tess )
|
|
{
|
|
RequireState( tess, T_DORMANT );
|
|
memFree( tess );
|
|
}
|
|
|
|
|
|
void GLAPIENTRY
|
|
gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value )
|
|
{
|
|
GLenum windingRule;
|
|
|
|
switch( which ) {
|
|
case GLU_TESS_TOLERANCE:
|
|
if( value < 0.0 || value > 1.0 ) break;
|
|
tess->relTolerance = value;
|
|
return;
|
|
|
|
case GLU_TESS_WINDING_RULE:
|
|
windingRule = (GLenum) value;
|
|
if( windingRule != value ) break; /* not an integer */
|
|
|
|
switch( windingRule ) {
|
|
case GLU_TESS_WINDING_ODD:
|
|
case GLU_TESS_WINDING_NONZERO:
|
|
case GLU_TESS_WINDING_POSITIVE:
|
|
case GLU_TESS_WINDING_NEGATIVE:
|
|
case GLU_TESS_WINDING_ABS_GEQ_TWO:
|
|
tess->windingRule = windingRule;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
case GLU_TESS_BOUNDARY_ONLY:
|
|
tess->boundaryOnly = (value != 0);
|
|
return;
|
|
|
|
default:
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
|
|
return;
|
|
}
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE );
|
|
}
|
|
|
|
/* Returns tessellator property */
|
|
void GLAPIENTRY
|
|
gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value )
|
|
{
|
|
switch (which) {
|
|
case GLU_TESS_TOLERANCE:
|
|
/* tolerance should be in range [0..1] */
|
|
assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0);
|
|
*value= tess->relTolerance;
|
|
break;
|
|
case GLU_TESS_WINDING_RULE:
|
|
assert(tess->windingRule == GLU_TESS_WINDING_ODD ||
|
|
tess->windingRule == GLU_TESS_WINDING_NONZERO ||
|
|
tess->windingRule == GLU_TESS_WINDING_POSITIVE ||
|
|
tess->windingRule == GLU_TESS_WINDING_NEGATIVE ||
|
|
tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO);
|
|
*value= tess->windingRule;
|
|
break;
|
|
case GLU_TESS_BOUNDARY_ONLY:
|
|
assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE);
|
|
*value= tess->boundaryOnly;
|
|
break;
|
|
default:
|
|
*value= 0.0;
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
|
|
break;
|
|
}
|
|
} /* gluGetTessProperty() */
|
|
|
|
void GLAPIENTRY
|
|
gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z )
|
|
{
|
|
tess->normal[0] = x;
|
|
tess->normal[1] = y;
|
|
tess->normal[2] = z;
|
|
}
|
|
|
|
void GLAPIENTRY
|
|
gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn)
|
|
{
|
|
switch( which ) {
|
|
case GLU_TESS_BEGIN:
|
|
tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn;
|
|
return;
|
|
case GLU_TESS_BEGIN_DATA:
|
|
tess->callBeginData = (fn == NULL) ?
|
|
&__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
|
|
return;
|
|
case GLU_TESS_EDGE_FLAG:
|
|
tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag :
|
|
(void (GLAPIENTRY *)(GLboolean)) fn;
|
|
/* If the client wants boundary edges to be flagged,
|
|
* we render everything as separate triangles (no strips or fans).
|
|
*/
|
|
tess->flagBoundary = (fn != NULL);
|
|
return;
|
|
case GLU_TESS_EDGE_FLAG_DATA:
|
|
tess->callEdgeFlagData= (fn == NULL) ?
|
|
&__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn;
|
|
/* If the client wants boundary edges to be flagged,
|
|
* we render everything as separate triangles (no strips or fans).
|
|
*/
|
|
tess->flagBoundary = (fn != NULL);
|
|
return;
|
|
case GLU_TESS_VERTEX:
|
|
tess->callVertex = (fn == NULL) ? &noVertex :
|
|
(void (GLAPIENTRY *)(void *)) fn;
|
|
return;
|
|
case GLU_TESS_VERTEX_DATA:
|
|
tess->callVertexData = (fn == NULL) ?
|
|
&__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn;
|
|
return;
|
|
case GLU_TESS_END:
|
|
tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn;
|
|
return;
|
|
case GLU_TESS_END_DATA:
|
|
tess->callEndData = (fn == NULL) ? &__gl_noEndData :
|
|
(void (GLAPIENTRY *)(void *)) fn;
|
|
return;
|
|
case GLU_TESS_ERROR:
|
|
tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn;
|
|
return;
|
|
case GLU_TESS_ERROR_DATA:
|
|
tess->callErrorData = (fn == NULL) ?
|
|
&__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
|
|
return;
|
|
case GLU_TESS_COMBINE:
|
|
tess->callCombine = (fn == NULL) ? &noCombine :
|
|
(void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn;
|
|
return;
|
|
case GLU_TESS_COMBINE_DATA:
|
|
tess->callCombineData = (fn == NULL) ? &__gl_noCombineData :
|
|
(void (GLAPIENTRY *)(GLdouble [3],
|
|
void *[4],
|
|
GLfloat [4],
|
|
void **,
|
|
void *)) fn;
|
|
return;
|
|
case GLU_TESS_MESH:
|
|
tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn;
|
|
return;
|
|
default:
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
|
|
{
|
|
GLUhalfEdge *e;
|
|
|
|
e = tess->lastEdge;
|
|
if( e == NULL ) {
|
|
/* Make a self-loop (one vertex, one edge). */
|
|
|
|
e = __gl_meshMakeEdge( tess->mesh );
|
|
if (e == NULL) return 0;
|
|
if ( !__gl_meshSplice( e, e->Sym ) ) return 0;
|
|
} else {
|
|
/* Create a new vertex and edge which immediately follow e
|
|
* in the ordering around the left face.
|
|
*/
|
|
if (__gl_meshSplitEdge( e ) == NULL) return 0;
|
|
e = e->Lnext;
|
|
}
|
|
|
|
/* The new vertex is now e->Org. */
|
|
e->Org->data = data;
|
|
e->Org->coords[0] = coords[0];
|
|
e->Org->coords[1] = coords[1];
|
|
e->Org->coords[2] = coords[2];
|
|
|
|
/* The winding of an edge says how the winding number changes as we
|
|
* cross from the edge''s right face to its left face. We add the
|
|
* vertices in such an order that a CCW contour will add +1 to
|
|
* the winding number of the region inside the contour.
|
|
*/
|
|
e->winding = 1;
|
|
e->Sym->winding = -1;
|
|
|
|
tess->lastEdge = e;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
|
|
{
|
|
CachedVertex *v = &tess->cache[tess->cacheCount];
|
|
|
|
v->data = data;
|
|
v->coords[0] = coords[0];
|
|
v->coords[1] = coords[1];
|
|
v->coords[2] = coords[2];
|
|
++tess->cacheCount;
|
|
}
|
|
|
|
|
|
static int EmptyCache( GLUtesselator *tess )
|
|
{
|
|
CachedVertex *v = tess->cache;
|
|
CachedVertex *vLast;
|
|
|
|
tess->mesh = __gl_meshNewMesh();
|
|
if (tess->mesh == NULL) return 0;
|
|
|
|
for( vLast = v + tess->cacheCount; v < vLast; ++v ) {
|
|
if ( !AddVertex( tess, v->coords, v->data ) ) return 0;
|
|
}
|
|
tess->cacheCount = 0;
|
|
tess->emptyCache = FALSE;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void GLAPIENTRY
|
|
gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
|
|
{
|
|
int i, tooLarge = FALSE;
|
|
GLdouble x, clamped[3];
|
|
|
|
RequireState( tess, T_IN_CONTOUR );
|
|
|
|
if( tess->emptyCache ) {
|
|
if ( !EmptyCache( tess ) ) {
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
|
|
return;
|
|
}
|
|
tess->lastEdge = NULL;
|
|
}
|
|
for( i = 0; i < 3; ++i ) {
|
|
x = coords[i];
|
|
if( x < - GLU_TESS_MAX_COORD ) {
|
|
x = - GLU_TESS_MAX_COORD;
|
|
tooLarge = TRUE;
|
|
}
|
|
if( x > GLU_TESS_MAX_COORD ) {
|
|
x = GLU_TESS_MAX_COORD;
|
|
tooLarge = TRUE;
|
|
}
|
|
clamped[i] = x;
|
|
}
|
|
if( tooLarge ) {
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE );
|
|
}
|
|
|
|
if( tess->mesh == NULL ) {
|
|
if( tess->cacheCount < TESS_MAX_CACHE ) {
|
|
CacheVertex( tess, clamped, data );
|
|
return;
|
|
}
|
|
if ( !EmptyCache( tess ) ) {
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
|
|
return;
|
|
}
|
|
}
|
|
if ( !AddVertex( tess, clamped, data ) ) {
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
|
|
}
|
|
}
|
|
|
|
|
|
void GLAPIENTRY
|
|
gluTessBeginPolygon( GLUtesselator *tess, void *data )
|
|
{
|
|
RequireState( tess, T_DORMANT );
|
|
|
|
tess->state = T_IN_POLYGON;
|
|
tess->cacheCount = 0;
|
|
tess->emptyCache = FALSE;
|
|
tess->mesh = NULL;
|
|
|
|
tess->polygonData= data;
|
|
}
|
|
|
|
|
|
void GLAPIENTRY
|
|
gluTessBeginContour( GLUtesselator *tess )
|
|
{
|
|
RequireState( tess, T_IN_POLYGON );
|
|
|
|
tess->state = T_IN_CONTOUR;
|
|
tess->lastEdge = NULL;
|
|
if( tess->cacheCount > 0 ) {
|
|
/* Just set a flag so we don't get confused by empty contours
|
|
* -- these can be generated accidentally with the obsolete
|
|
* NextContour() interface.
|
|
*/
|
|
tess->emptyCache = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
void GLAPIENTRY
|
|
gluTessEndContour( GLUtesselator *tess )
|
|
{
|
|
RequireState( tess, T_IN_CONTOUR );
|
|
tess->state = T_IN_POLYGON;
|
|
}
|
|
|
|
void GLAPIENTRY
|
|
gluTessEndPolygon( GLUtesselator *tess )
|
|
{
|
|
GLUmesh *mesh;
|
|
|
|
if (setjmp(tess->env) != 0) {
|
|
/* come back here if out of memory */
|
|
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
|
|
return;
|
|
}
|
|
|
|
RequireState( tess, T_IN_POLYGON );
|
|
tess->state = T_DORMANT;
|
|
|
|
if( tess->mesh == NULL ) {
|
|
if( ! tess->flagBoundary && tess->callMesh == &noMesh ) {
|
|
|
|
/* Try some special code to make the easy cases go quickly
|
|
* (eg. convex polygons). This code does NOT handle multiple contours,
|
|
* intersections, edge flags, and of course it does not generate
|
|
* an explicit mesh either.
|
|
*/
|
|
if( __gl_renderCache( tess )) {
|
|
tess->polygonData= NULL;
|
|
return;
|
|
}
|
|
}
|
|
if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/
|
|
}
|
|
|
|
/* Determine the polygon normal and project vertices onto the plane
|
|
* of the polygon.
|
|
*/
|
|
__gl_projectPolygon( tess );
|
|
|
|
/* __gl_computeInterior( tess ) computes the planar arrangement specified
|
|
* by the given contours, and further subdivides this arrangement
|
|
* into regions. Each region is marked "inside" if it belongs
|
|
* to the polygon, according to the rule given by tess->windingRule.
|
|
* Each interior region is guaranteed be monotone.
|
|
*/
|
|
if ( !__gl_computeInterior( tess ) ) {
|
|
longjmp(tess->env,1); /* could've used a label */
|
|
}
|
|
|
|
mesh = tess->mesh;
|
|
if( ! tess->fatalError ) {
|
|
int rc = 1;
|
|
|
|
/* If the user wants only the boundary contours, we throw away all edges
|
|
* except those which separate the interior from the exterior.
|
|
* Otherwise we tessellate all the regions marked "inside".
|
|
*/
|
|
if( tess->boundaryOnly ) {
|
|
rc = __gl_meshSetWindingNumber( mesh, 1, TRUE );
|
|
} else {
|
|
rc = __gl_meshTessellateInterior( mesh );
|
|
}
|
|
if (rc == 0) longjmp(tess->env,1); /* could've used a label */
|
|
|
|
__gl_meshCheckMesh( mesh );
|
|
|
|
if( tess->callBegin != &noBegin || tess->callEnd != &noEnd
|
|
|| tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag
|
|
|| tess->callBeginData != &__gl_noBeginData
|
|
|| tess->callEndData != &__gl_noEndData
|
|
|| tess->callVertexData != &__gl_noVertexData
|
|
|| tess->callEdgeFlagData != &__gl_noEdgeFlagData )
|
|
{
|
|
if( tess->boundaryOnly ) {
|
|
__gl_renderBoundary( tess, mesh ); /* output boundary contours */
|
|
} else {
|
|
__gl_renderMesh( tess, mesh ); /* output strips and fans */
|
|
}
|
|
}
|
|
if( tess->callMesh != &noMesh ) {
|
|
|
|
/* Throw away the exterior faces, so that all faces are interior.
|
|
* This way the user doesn't have to check the "inside" flag,
|
|
* and we don't need to even reveal its existence. It also leaves
|
|
* the freedom for an implementation to not generate the exterior
|
|
* faces in the first place.
|
|
*/
|
|
__gl_meshDiscardExterior( mesh );
|
|
(*tess->callMesh)( mesh ); /* user wants the mesh itself */
|
|
tess->mesh = NULL;
|
|
tess->polygonData= NULL;
|
|
return;
|
|
}
|
|
}
|
|
__gl_meshDeleteMesh( mesh );
|
|
tess->polygonData= NULL;
|
|
tess->mesh = NULL;
|
|
}
|
|
|
|
|
|
/*XXXblythe unused function*/
|
|
#if 0
|
|
void GLAPIENTRY
|
|
gluDeleteMesh( GLUmesh *mesh )
|
|
{
|
|
__gl_meshDeleteMesh( mesh );
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/*******************************************************/
|
|
|
|
/* Obsolete calls -- for backward compatibility */
|
|
|
|
void GLAPIENTRY
|
|
gluBeginPolygon( GLUtesselator *tess )
|
|
{
|
|
gluTessBeginPolygon( tess, NULL );
|
|
gluTessBeginContour( tess );
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
void GLAPIENTRY
|
|
gluNextContour( GLUtesselator *tess, GLenum type )
|
|
{
|
|
gluTessEndContour( tess );
|
|
gluTessBeginContour( tess );
|
|
}
|
|
|
|
|
|
void GLAPIENTRY
|
|
gluEndPolygon( GLUtesselator *tess )
|
|
{
|
|
gluTessEndContour( tess );
|
|
gluTessEndPolygon( tess );
|
|
}
|