123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- /*
- * 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 "../prboom/SDL_opengl.h" // JDC
- //#include "gluos.h"
- #include <assert.h>
- #include <stddef.h>
- #include "mesh.h"
- #include "tess.h"
- #include "render.h"
- #define TRUE 1
- #define FALSE 0
- /* This structure remembers the information we need about a primitive
- * to be able to render it later, once we have determined which
- * primitive is able to use the most triangles.
- */
- struct FaceCount {
- long size; /* number of triangles used */
- GLUhalfEdge *eStart; /* edge where this primitive starts */
- void (*render)(GLUtesselator *, GLUhalfEdge *, long);
- /* routine to render this primitive */
- };
- static struct FaceCount MaximumFan( GLUhalfEdge *eOrig );
- static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig );
- static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
- static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
- static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart,
- long size );
- static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig );
- static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
- /************************ Strips and Fans decomposition ******************/
- /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
- * fans, strips, and separate triangles. A substantial effort is made
- * to use as few rendering primitives as possible (ie. to make the fans
- * and strips as large as possible).
- *
- * The rendering output is provided as callbacks (see the api).
- */
- void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh )
- {
- GLUface *f;
- /* Make a list of separate triangles so we can render them all at once */
- tess->lonelyTriList = NULL;
- for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
- f->marked = FALSE;
- }
- for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
- /* We examine all faces in an arbitrary order. Whenever we find
- * an unprocessed face F, we output a group of faces including F
- * whose size is maximum.
- */
- if( f->inside && ! f->marked ) {
- RenderMaximumFaceGroup( tess, f );
- assert( f->marked );
- }
- }
- if( tess->lonelyTriList != NULL ) {
- RenderLonelyTriangles( tess, tess->lonelyTriList );
- tess->lonelyTriList = NULL;
- }
- }
- static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
- {
- /* We want to find the largest triangle fan or strip of unmarked faces
- * which includes the given face fOrig. There are 3 possible fans
- * passing through fOrig (one centered at each vertex), and 3 possible
- * strips (one for each CCW permutation of the vertices). Our strategy
- * is to try all of these, and take the primitive which uses the most
- * triangles (a greedy approach).
- */
- GLUhalfEdge *e = fOrig->anEdge;
- struct FaceCount max, newFace;
- max.size = 1;
- max.eStart = e;
- max.render = &RenderTriangle;
- if( ! tess->flagBoundary ) {
- newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; }
- newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
- newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
- newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; }
- newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
- newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
- }
- (*(max.render))( tess, max.eStart, max.size );
- }
- /* Macros which keep track of faces we have marked temporarily, and allow
- * us to backtrack when necessary. With triangle fans, this is not
- * really necessary, since the only awkward case is a loop of triangles
- * around a single origin vertex. However with strips the situation is
- * more complicated, and we need a general tracking method like the
- * one here.
- */
- #define Marked(f) (! (f)->inside || (f)->marked)
- #define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE)
- #define FreeTrail(t) if( 1 ) { \
- while( (t) != NULL ) { \
- (t)->marked = FALSE; t = (t)->trail; \
- } \
- } else /* absorb trailing semicolon */
- static struct FaceCount MaximumFan( GLUhalfEdge *eOrig )
- {
- /* eOrig->Lface is the face we want to render. We want to find the size
- * of a maximal fan around eOrig->Org. To do this we just walk around
- * the origin vertex as far as possible in both directions.
- */
- struct FaceCount newFace = { 0, NULL, &RenderFan };
- GLUface *trail = NULL;
- GLUhalfEdge *e;
- for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) {
- AddToTrail( e->Lface, trail );
- ++newFace.size;
- }
- for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) {
- AddToTrail( e->Rface, trail );
- ++newFace.size;
- }
- newFace.eStart = e;
- /*LINTED*/
- FreeTrail( trail );
- return newFace;
- }
- #define IsEven(n) (((n) & 1) == 0)
- static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig )
- {
- /* Here we are looking for a maximal strip that contains the vertices
- * eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
- * reverse, such that all triangles are oriented CCW).
- *
- * Again we walk forward and backward as far as possible. However for
- * strips there is a twist: to get CCW orientations, there must be
- * an *even* number of triangles in the strip on one side of eOrig.
- * We walk the strip starting on a side with an even number of triangles;
- * if both side have an odd number, we are forced to shorten one side.
- */
- struct FaceCount newFace = { 0, NULL, &RenderStrip };
- long headSize = 0, tailSize = 0;
- GLUface *trail = NULL;
- GLUhalfEdge *e, *eTail, *eHead;
- for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) {
- AddToTrail( e->Lface, trail );
- ++tailSize;
- e = e->Dprev;
- if( Marked( e->Lface )) break;
- AddToTrail( e->Lface, trail );
- }
- eTail = e;
- for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) {
- AddToTrail( e->Rface, trail );
- ++headSize;
- e = e->Oprev;
- if( Marked( e->Rface )) break;
- AddToTrail( e->Rface, trail );
- }
- eHead = e;
- newFace.size = tailSize + headSize;
- if( IsEven( tailSize )) {
- newFace.eStart = eTail->Sym;
- } else if( IsEven( headSize )) {
- newFace.eStart = eHead;
- } else {
- /* Both sides have odd length, we must shorten one of them. In fact,
- * we must start from eHead to guarantee inclusion of eOrig->Lface.
- */
- --newFace.size;
- newFace.eStart = eHead->Onext;
- }
- /*LINTED*/
- FreeTrail( trail );
- return newFace;
- }
- static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size )
- {
- /* Just add the triangle to a triangle list, so we can render all
- * the separate triangles at once.
- */
- assert( size == 1 );
- AddToTrail( e->Lface, tess->lonelyTriList );
- }
- static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f )
- {
- /* Now we render all the separate triangles which could not be
- * grouped into a triangle fan or strip.
- */
- GLUhalfEdge *e;
- int newState;
- int edgeState = -1; /* force edge state output for first vertex */
- CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES );
- for( ; f != NULL; f = f->trail ) {
- /* Loop once for each edge (there will always be 3 edges) */
- e = f->anEdge;
- do {
- if( tess->flagBoundary ) {
- /* Set the "edge state" to TRUE just before we output the
- * first vertex of each edge on the polygon boundary.
- */
- newState = ! e->Rface->inside;
- if( edgeState != newState ) {
- edgeState = newState;
- CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState );
- }
- }
- CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
- e = e->Lnext;
- } while( e != f->anEdge );
- }
- CALL_END_OR_END_DATA();
- }
- static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size )
- {
- /* Render as many CCW triangles as possible in a fan starting from
- * edge "e". The fan *should* contain exactly "size" triangles
- * (otherwise we've goofed up somewhere).
- */
- CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN );
- CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
- CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
- while( ! Marked( e->Lface )) {
- e->Lface->marked = TRUE;
- --size;
- e = e->Onext;
- CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
- }
- assert( size == 0 );
- CALL_END_OR_END_DATA();
- }
- static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size )
- {
- /* Render as many CCW triangles as possible in a strip starting from
- * edge "e". The strip *should* contain exactly "size" triangles
- * (otherwise we've goofed up somewhere).
- */
- CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP );
- CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
- CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
- while( ! Marked( e->Lface )) {
- e->Lface->marked = TRUE;
- --size;
- e = e->Dprev;
- CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
- if( Marked( e->Lface )) break;
- e->Lface->marked = TRUE;
- --size;
- e = e->Onext;
- CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
- }
- assert( size == 0 );
- CALL_END_OR_END_DATA();
- }
- /************************ Boundary contour decomposition ******************/
- /* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one
- * contour for each face marked "inside". The rendering output is
- * provided as callbacks (see the api).
- */
- void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh )
- {
- GLUface *f;
- GLUhalfEdge *e;
- for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
- if( f->inside ) {
- CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP );
- e = f->anEdge;
- do {
- CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
- e = e->Lnext;
- } while( e != f->anEdge );
- CALL_END_OR_END_DATA();
- }
- }
- }
- /************************ Quick-and-dirty decomposition ******************/
- #define SIGN_INCONSISTENT 2
- static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
- /*
- * If check==FALSE, we compute the polygon normal and place it in norm[].
- * If check==TRUE, we check that each triangle in the fan from v0 has a
- * consistent orientation with respect to norm[]. If triangles are
- * consistently oriented CCW, return 1; if CW, return -1; if all triangles
- * are degenerate return 0; otherwise (no consistent orientation) return
- * SIGN_INCONSISTENT.
- */
- {
- CachedVertex *v0 = tess->cache;
- CachedVertex *vn = v0 + tess->cacheCount;
- CachedVertex *vc;
- GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
- int sign = 0;
- /* Find the polygon normal. It is important to get a reasonable
- * normal even when the polygon is self-intersecting (eg. a bowtie).
- * Otherwise, the computed normal could be very tiny, but perpendicular
- * to the true plane of the polygon due to numerical noise. Then all
- * the triangles would appear to be degenerate and we would incorrectly
- * decompose the polygon as a fan (or simply not render it at all).
- *
- * We use a sum-of-triangles normal algorithm rather than the more
- * efficient sum-of-trapezoids method (used in CheckOrientation()
- * in normal.c). This lets us explicitly reverse the signed area
- * of some triangles to get a reasonable normal in the self-intersecting
- * case.
- */
- if( ! check ) {
- norm[0] = norm[1] = norm[2] = 0.0;
- }
- vc = v0 + 1;
- xc = vc->coords[0] - v0->coords[0];
- yc = vc->coords[1] - v0->coords[1];
- zc = vc->coords[2] - v0->coords[2];
- while( ++vc < vn ) {
- xp = xc; yp = yc; zp = zc;
- xc = vc->coords[0] - v0->coords[0];
- yc = vc->coords[1] - v0->coords[1];
- zc = vc->coords[2] - v0->coords[2];
- /* Compute (vp - v0) cross (vc - v0) */
- n[0] = yp*zc - zp*yc;
- n[1] = zp*xc - xp*zc;
- n[2] = xp*yc - yp*xc;
- dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
- if( ! check ) {
- /* Reverse the contribution of back-facing triangles to get
- * a reasonable normal for self-intersecting polygons (see above)
- */
- if( dot >= 0 ) {
- norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2];
- } else {
- norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2];
- }
- } else if( dot != 0 ) {
- /* Check the new orientation for consistency with previous triangles */
- if( dot > 0 ) {
- if( sign < 0 ) return SIGN_INCONSISTENT;
- sign = 1;
- } else {
- if( sign > 0 ) return SIGN_INCONSISTENT;
- sign = -1;
- }
- }
- }
- return sign;
- }
- /* __gl_renderCache( tess ) takes a single contour and tries to render it
- * as a triangle fan. This handles convex polygons, as well as some
- * non-convex polygons if we get lucky.
- *
- * Returns TRUE if the polygon was successfully rendered. The rendering
- * output is provided as callbacks (see the api).
- */
- GLboolean __gl_renderCache( GLUtesselator *tess )
- {
- CachedVertex *v0 = tess->cache;
- CachedVertex *vn = v0 + tess->cacheCount;
- CachedVertex *vc;
- GLdouble norm[3];
- int sign;
- if( tess->cacheCount < 3 ) {
- /* Degenerate contour -- no output */
- return TRUE;
- }
- norm[0] = tess->normal[0];
- norm[1] = tess->normal[1];
- norm[2] = tess->normal[2];
- if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
- ComputeNormal( tess, norm, FALSE );
- }
- sign = ComputeNormal( tess, norm, TRUE );
- if( sign == SIGN_INCONSISTENT ) {
- /* Fan triangles did not have a consistent orientation */
- return FALSE;
- }
- if( sign == 0 ) {
- /* All triangles were degenerate */
- return TRUE;
- }
- /* Make sure we do the right thing for each winding rule */
- switch( tess->windingRule ) {
- case GLU_TESS_WINDING_ODD:
- case GLU_TESS_WINDING_NONZERO:
- break;
- case GLU_TESS_WINDING_POSITIVE:
- if( sign < 0 ) return TRUE;
- break;
- case GLU_TESS_WINDING_NEGATIVE:
- if( sign > 0 ) return TRUE;
- break;
- case GLU_TESS_WINDING_ABS_GEQ_TWO:
- return TRUE;
- }
- CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
- : (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
- : GL_TRIANGLES );
- CALL_VERTEX_OR_VERTEX_DATA( v0->data );
- if( sign > 0 ) {
- for( vc = v0+1; vc < vn; ++vc ) {
- CALL_VERTEX_OR_VERTEX_DATA( vc->data );
- }
- } else {
- for( vc = vn-1; vc > v0; --vc ) {
- CALL_VERTEX_OR_VERTEX_DATA( vc->data );
- }
- }
- CALL_END_OR_END_DATA();
- return TRUE;
- }
|