]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/surface_meta.c
eol style
[xonotic/netradiant.git] / tools / quake3 / q3map2 / surface_meta.c
index 74307a762c369ec52d5c4033f82401c6fafeefca..f0c3d0a02eb625b9b8cba840d5f89f637853d78c 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-\r
-----------------------------------------------------------------------------------\r
-\r
-This code has been altered significantly from its original form, to support\r
-several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
-\r
-------------------------------------------------------------------------------- */\r
-\r
-\r
-\r
-/* marker */\r
-#define SURFACE_META_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-#define LIGHTMAP_EXCEEDED      -1\r
-#define S_EXCEEDED                     -2\r
-#define T_EXCEEDED                     -3\r
-#define ST_EXCEEDED                    -4\r
-#define UNSUITABLE_TRIANGLE    -10\r
-#define VERTS_EXCEEDED         -1000\r
-#define INDEXES_EXCEEDED       -2000\r
-\r
-#define GROW_META_VERTS                1024\r
-#define GROW_META_TRIANGLES    1024\r
-\r
-static int                                     numMetaSurfaces, numPatchMetaSurfaces;\r
-\r
-static int                                     maxMetaVerts = 0;\r
-static int                                     numMetaVerts = 0;\r
-static int                                     firstSearchMetaVert = 0;\r
-static bspDrawVert_t           *metaVerts = NULL;\r
-\r
-static int                                     maxMetaTriangles = 0;\r
-static int                                     numMetaTriangles = 0;\r
-static metaTriangle_t          *metaTriangles = NULL;\r
-\r
-\r
-\r
-/*\r
-ClearMetaVertexes()\r
-called before staring a new entity to clear out the triangle list\r
-*/\r
-\r
-void ClearMetaTriangles( void )\r
-{\r
-       numMetaVerts = 0;\r
-       numMetaTriangles = 0;\r
-}\r
-\r
-\r
-\r
-/*\r
-FindMetaVertex()\r
-finds a matching metavertex in the global list, returning its index\r
-*/\r
-\r
-static int FindMetaVertex( bspDrawVert_t *src )\r
-{\r
-       int                     i;\r
-       bspDrawVert_t   *v, *temp;\r
-       \r
-       \r
-       /* try to find an existing drawvert */\r
-       for( i = firstSearchMetaVert, v = &metaVerts[ i ]; i < numMetaVerts; i++, v++ )\r
-       {\r
-               if( memcmp( src, v, sizeof( bspDrawVert_t ) ) == 0 )\r
-                       return i;\r
-       }\r
-       \r
-       /* enough space? */\r
-       if( numMetaVerts >= maxMetaVerts )\r
-       {\r
-               /* reallocate more room */\r
-               maxMetaVerts += GROW_META_VERTS;\r
-               temp = safe_malloc( maxMetaVerts * sizeof( bspDrawVert_t ) );\r
-               if( metaVerts != NULL )\r
-               {\r
-                       memcpy( temp, metaVerts, numMetaVerts * sizeof( bspDrawVert_t ) );\r
-                       free( metaVerts );\r
-               }\r
-               metaVerts = temp;\r
-       }\r
-       \r
-       /* add the triangle */\r
-       memcpy( &metaVerts[ numMetaVerts ], src, sizeof( bspDrawVert_t ) );\r
-       numMetaVerts++;\r
-       \r
-       /* return the count */\r
-       return (numMetaVerts - 1);\r
-}\r
-\r
-\r
-\r
-/*\r
-AddMetaTriangle()\r
-adds a new meta triangle, allocating more memory if necessary\r
-*/\r
-\r
-static int AddMetaTriangle( void )\r
-{\r
-       metaTriangle_t  *temp;\r
-       \r
-       \r
-       /* enough space? */\r
-       if( numMetaTriangles >= maxMetaTriangles )\r
-       {\r
-               /* reallocate more room */\r
-               maxMetaTriangles += GROW_META_TRIANGLES;\r
-               temp = safe_malloc( maxMetaTriangles * sizeof( metaTriangle_t ) );\r
-               if( metaTriangles != NULL )\r
-               {\r
-                       memcpy( temp, metaTriangles, numMetaTriangles * sizeof( metaTriangle_t ) );\r
-                       free( metaTriangles );\r
-               }\r
-               metaTriangles = temp;\r
-       }\r
-       \r
-       /* increment and return */\r
-       numMetaTriangles++;\r
-       return numMetaTriangles - 1;\r
-}\r
-\r
-\r
-\r
-/*\r
-FindMetaTriangle()\r
-finds a matching metatriangle in the global list,\r
-otherwise adds it and returns the index to the metatriangle\r
-*/\r
-\r
-int FindMetaTriangle( metaTriangle_t *src, bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *c, int planeNum )\r
-{\r
-       int                             triIndex;\r
-       vec3_t                  dir;\r
-\r
-       \r
-       \r
-       /* detect degenerate triangles fixme: do something proper here */\r
-       VectorSubtract( a->xyz, b->xyz, dir );\r
-       if( VectorLength( dir ) < 0.125f )\r
-               return -1;\r
-       VectorSubtract( b->xyz, c->xyz, dir );\r
-       if( VectorLength( dir ) < 0.125f )\r
-               return -1;\r
-       VectorSubtract( c->xyz, a->xyz, dir );\r
-       if( VectorLength( dir ) < 0.125f )\r
-               return -1;\r
-       \r
-       /* find plane */\r
-       if( planeNum >= 0 )\r
-       {\r
-               /* because of precision issues with small triangles, try to use the specified plane */\r
-               src->planeNum = planeNum;\r
-               VectorCopy( mapplanes[ planeNum ].normal, src->plane );\r
-               src->plane[ 3 ] = mapplanes[ planeNum ].dist;\r
-       }\r
-       else\r
-       {\r
-               /* calculate a plane from the triangle's points (and bail if a plane can't be constructed) */\r
-               src->planeNum = -1;\r
-               if( PlaneFromPoints( src->plane, a->xyz, b->xyz, c->xyz ) == qfalse )\r
-                       return -1;\r
-       }\r
-       \r
-       /* ydnar 2002-10-03: repair any bogus normals (busted ase import kludge) */\r
-       if( VectorLength( a->normal ) <= 0.0f )\r
-               VectorCopy( src->plane, a->normal );\r
-       if( VectorLength( b->normal ) <= 0.0f )\r
-               VectorCopy( src->plane, b->normal );\r
-       if( VectorLength( c->normal ) <= 0.0f )\r
-               VectorCopy( src->plane, c->normal );\r
-       \r
-       /* ydnar 2002-10-04: set lightmap axis if not already set */\r
-       if( !(src->si->compileFlags & C_VERTEXLIT) &&\r
-               src->lightmapAxis[ 0 ] == 0.0f && src->lightmapAxis[ 1 ] == 0.0f && src->lightmapAxis[ 2 ] == 0.0f )\r
-       {\r
-               /* the shader can specify an explicit lightmap axis */\r
-               if( src->si->lightmapAxis[ 0 ] || src->si->lightmapAxis[ 1 ] || src->si->lightmapAxis[ 2 ] )\r
-                       VectorCopy( src->si->lightmapAxis, src->lightmapAxis );\r
-               \r
-               /* new axis-finding code */\r
-               else\r
-                       CalcLightmapAxis( src->plane, src->lightmapAxis );\r
-       }\r
-       \r
-       /* fill out the src triangle */\r
-       src->indexes[ 0 ] = FindMetaVertex( a );\r
-       src->indexes[ 1 ] = FindMetaVertex( b );\r
-       src->indexes[ 2 ] = FindMetaVertex( c );\r
-       \r
-       /* try to find an existing triangle */\r
-       #ifdef USE_EXHAUSTIVE_SEARCH\r
-       {\r
-               int                             i;\r
-               metaTriangle_t  *tri;\r
-               \r
-               \r
-               for( i = 0, tri = metaTriangles; i < numMetaTriangles; i++, tri++ )\r
-               {\r
-                       if( memcmp( src, tri, sizeof( metaTriangle_t ) ) == 0 )\r
-                               return i;\r
-               }\r
-       }\r
-       #endif\r
-       \r
-       /* get a new triangle */\r
-       triIndex = AddMetaTriangle();\r
-       \r
-       /* add the triangle */\r
-       memcpy( &metaTriangles[ triIndex ], src, sizeof( metaTriangle_t ) );\r
-       \r
-       /* return the triangle index */\r
-       return triIndex;\r
-}\r
-\r
-\r
-\r
-/*\r
-SurfaceToMetaTriangles()\r
-converts a classified surface to metatriangles\r
-*/\r
-\r
-static void SurfaceToMetaTriangles( mapDrawSurface_t *ds )\r
-{\r
-       int                             i;\r
-       metaTriangle_t  src;\r
-       bspDrawVert_t   a, b, c;\r
-       \r
-       \r
-       /* only handle certain types of surfaces */\r
-       if( ds->type != SURFACE_FACE &&\r
-               ds->type != SURFACE_META &&\r
-               ds->type != SURFACE_FORCED_META &&\r
-               ds->type != SURFACE_DECAL )\r
-               return;\r
-       \r
-       /* speed at the expense of memory */\r
-       firstSearchMetaVert = numMetaVerts;\r
-       \r
-       /* only handle valid surfaces */\r
-       if( ds->type != SURFACE_BAD && ds->numVerts >= 3 && ds->numIndexes >= 3 )\r
-       {\r
-               /* walk the indexes and create triangles */\r
-               for( i = 0; i < ds->numIndexes; i += 3 )\r
-               {\r
-                       /* sanity check the indexes */\r
-                       if( ds->indexes[ i ] == ds->indexes[ i + 1 ] ||\r
-                               ds->indexes[ i ] == ds->indexes[ i + 2 ] ||\r
-                               ds->indexes[ i + 1 ] == ds->indexes[ i + 2 ] )\r
-                       {\r
-                               //%     Sys_Printf( "%d! ", ds->numVerts );\r
-                               continue;\r
-                       }\r
-                       \r
-                       /* build a metatriangle */\r
-                       src.si = ds->shaderInfo;\r
-                       src.side = (ds->sideRef != NULL ? ds->sideRef->side : NULL);\r
-                       src.entityNum = ds->entityNum;\r
-                       src.surfaceNum = ds->surfaceNum;\r
-                       src.planeNum = ds->planeNum;\r
-                       src.castShadows = ds->castShadows;\r
-                       src.recvShadows = ds->recvShadows;\r
-                       src.fogNum = ds->fogNum;\r
-                       src.sampleSize = ds->sampleSize;\r
-                       VectorCopy( ds->lightmapAxis, src.lightmapAxis );\r
-                       \r
-                       /* copy drawverts */\r
-                       memcpy( &a, &ds->verts[ ds->indexes[ i ] ], sizeof( a ) );\r
-                       memcpy( &b, &ds->verts[ ds->indexes[ i + 1 ] ], sizeof( b ) );\r
-                       memcpy( &c, &ds->verts[ ds->indexes[ i + 2 ] ], sizeof( c ) );\r
-                       FindMetaTriangle( &src, &a, &b, &c, ds->planeNum );\r
-               }\r
-               \r
-               /* add to count */\r
-               numMetaSurfaces++;\r
-       }\r
-       \r
-       /* clear the surface (free verts and indexes, sets it to SURFACE_BAD) */\r
-       ClearSurface( ds );\r
-}\r
-\r
-\r
-\r
-/*\r
-TriangulatePatchSurface()\r
-creates triangles from a patch\r
-*/\r
-\r
-void TriangulatePatchSurface( mapDrawSurface_t *ds )\r
-{\r
-       int                                     iterations, x, y, pw[ 5 ], r;\r
-       mapDrawSurface_t        *dsNew;\r
-       mesh_t                          src, *subdivided, *mesh;\r
-       \r
-       \r
-       /* try to early out */\r
-       if( ds->numVerts == 0 || ds->type != SURFACE_PATCH || patchMeta == qfalse )\r
-               return;\r
-       \r
-       /* make a mesh from the drawsurf */ \r
-       src.width = ds->patchWidth;\r
-       src.height = ds->patchHeight;\r
-       src.verts = ds->verts;\r
-       //%     subdivided = SubdivideMesh( src, 8, 999 );\r
-       iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );\r
-       subdivided = SubdivideMesh2( src, iterations ); //%     ds->maxIterations\r
-       \r
-       /* fit it to the curve and remove colinear verts on rows/columns */\r
-       PutMeshOnCurve( *subdivided );\r
-       mesh = RemoveLinearMeshColumnsRows( subdivided );\r
-       FreeMesh( subdivided );\r
-       //% MakeMeshNormals( mesh );\r
-       \r
-       /* make a copy of the drawsurface */\r
-       dsNew = AllocDrawSurface( SURFACE_META );\r
-       memcpy( dsNew, ds, sizeof( *ds ) );\r
-       \r
-       /* if the patch is nonsolid, then discard it */\r
-       if( !(ds->shaderInfo->compileFlags & C_SOLID) )\r
-               ClearSurface( ds );\r
-       \r
-       /* set new pointer */\r
-       ds = dsNew;\r
-       \r
-       /* basic transmogrification */\r
-       ds->type = SURFACE_META;\r
-       ds->numIndexes = 0;\r
-       ds->indexes = safe_malloc( mesh->width * mesh->height * 6 * sizeof( int ) );\r
-       \r
-       /* copy the verts in */\r
-       ds->numVerts = (mesh->width * mesh->height);\r
-       ds->verts = mesh->verts;\r
-       \r
-       /* iterate through the mesh quads */\r
-       for( y = 0; y < (mesh->height - 1); y++ )\r
-       {\r
-               for( x = 0; x < (mesh->width - 1); x++ )\r
-               {\r
-                       /* set indexes */\r
-                       pw[ 0 ] = x + (y * mesh->width);\r
-                       pw[ 1 ] = x + ((y + 1) * mesh->width);\r
-                       pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
-                       pw[ 3 ] = x + 1 + (y * mesh->width);\r
-                       pw[ 4 ] = x + (y * mesh->width);        /* same as pw[ 0 ] */\r
-                       \r
-                       /* set radix */\r
-                       r = (x + y) & 1;\r
-                       \r
-                       /* make first triangle */\r
-                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];\r
-                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 1 ];\r
-                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];\r
-                       \r
-                       /* make second triangle */\r
-                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];\r
-                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];\r
-                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 3 ];\r
-               }\r
-       }\r
-       \r
-       /* free the mesh, but not the verts */\r
-       free( mesh );\r
-       \r
-       /* add to count */\r
-       numPatchMetaSurfaces++;\r
-       \r
-       /* classify it */\r
-       ClassifySurfaces( 1, ds );\r
-}\r
-\r
-\r
-\r
-/*\r
-FanFaceSurface() - ydnar\r
-creates a tri-fan from a brush face winding\r
-loosely based on SurfaceAsTriFan()\r
-*/\r
-\r
-void FanFaceSurface( mapDrawSurface_t *ds )\r
-{\r
-       int                             i, j, k, a, b, c, color[ MAX_LIGHTMAPS ][ 4 ];\r
-       bspDrawVert_t   *verts, *centroid, *dv;\r
-       double                  iv;\r
-       \r
-       \r
-       /* try to early out */\r
-       if( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) )\r
-               return;\r
-       \r
-       /* add a new vertex at the beginning of the surface */\r
-       verts = safe_malloc( (ds->numVerts + 1) * sizeof( bspDrawVert_t ) );\r
-       memset( verts, 0, sizeof( bspDrawVert_t ) );\r
-       memcpy( &verts[ 1 ], ds->verts, ds->numVerts * sizeof( bspDrawVert_t ) );\r
-       free( ds->verts );\r
-       ds->verts = verts;\r
-       \r
-       /* add up the drawverts to create a centroid */\r
-       centroid = &verts[ 0 ];\r
-       memset( color, 0,  4 * MAX_LIGHTMAPS * sizeof( int ) );\r
-       for( i = 1, dv = &verts[ 1 ]; i < (ds->numVerts + 1); i++, dv++ )\r
-       {\r
-               VectorAdd( centroid->xyz, dv->xyz, centroid->xyz );\r
-               VectorAdd( centroid->normal, dv->normal, centroid->normal );\r
-               for( j = 0; j < 4; j++ )\r
-               {\r
-                       for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                               color[ k ][ j ] += dv->color[ k ][ j ];\r
-                       if( j < 2 )\r
-                       {\r
-                               centroid->st[ j ] += dv->st[ j ];\r
-                               for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                                       centroid->lightmap[ k ][ j ] += dv->lightmap[ k ][ j ];\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* average the centroid */\r
-       iv = 1.0f / ds->numVerts;\r
-       VectorScale( centroid->xyz, iv, centroid->xyz );\r
-       if( VectorNormalize( centroid->normal, centroid->normal ) <= 0 )\r
-               VectorCopy( verts[ 1 ].normal, centroid->normal );\r
-       for( j = 0; j < 4; j++ )\r
-       {\r
-               for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-               {\r
-                       color[ k ][ j ] /= ds->numVerts;\r
-                       centroid->color[ k ][ j ] = (color[ k ][ j ] < 255.0f ? color[ k ][ j ] : 255);\r
-               }\r
-               if( j < 2 )\r
-               {\r
-                       centroid->st[ j ] *= iv;\r
-                       for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                               centroid->lightmap[ k ][ j ] *= iv;\r
-               }\r
-       }\r
-       \r
-       /* add to vert count */\r
-       ds->numVerts++;\r
-       \r
-       /* fill indexes in triangle fan order */\r
-       ds->numIndexes = 0;\r
-       ds->indexes = safe_malloc( ds->numVerts * 3 * sizeof( int ) );\r
-       for( i = 1; i < ds->numVerts; i++ )\r
-       {\r
-               a = 0;\r
-               b = i;\r
-               c = (i + 1) % ds->numVerts;\r
-               c = c ? c : 1;\r
-               ds->indexes[ ds->numIndexes++ ] = a;\r
-               ds->indexes[ ds->numIndexes++ ] = b;\r
-               ds->indexes[ ds->numIndexes++ ] = c;\r
-       }\r
-       \r
-       /* add to count */\r
-       numFanSurfaces++;\r
-\r
-       /* classify it */\r
-       ClassifySurfaces( 1, ds );\r
-}\r
-\r
-\r
-\r
-/*\r
-StripFaceSurface() - ydnar\r
-attempts to create a valid tri-strip w/o degenerate triangles from a brush face winding\r
-based on SurfaceAsTriStrip()\r
-*/\r
-\r
-#define MAX_INDEXES            1024\r
-\r
-void StripFaceSurface( mapDrawSurface_t *ds ) \r
-{\r
-       int                     i, r, least, rotate, numIndexes, ni, a, b, c, indexes[ MAX_INDEXES ];\r
-       vec_t           *v1, *v2;\r
-       \r
-       \r
-       /* try to early out  */\r
-       if( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) )\r
-               return;\r
-       \r
-       /* is this a simple triangle? */\r
-       if( ds->numVerts == 3 )\r
-       {\r
-               numIndexes = 3;\r
-               VectorSet( indexes, 0, 1, 2 );\r
-       }\r
-       else\r
-       {\r
-               /* ydnar: find smallest coordinate */\r
-               least = 0;\r
-               if( ds->shaderInfo != NULL && ds->shaderInfo->autosprite == qfalse )\r
-               {\r
-                       for( i = 0; i < ds->numVerts; i++ )\r
-                       {\r
-                               /* get points */\r
-                               v1 = ds->verts[ i ].xyz;\r
-                               v2 = ds->verts[ least ].xyz;\r
-                               \r
-                               /* compare */\r
-                               if( v1[ 0 ] < v2[ 0 ] ||\r
-                                       (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] < v2[ 1 ]) ||\r
-                                       (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] == v2[ 1 ] && v1[ 2 ] < v2[ 2 ]) )\r
-                                       least = i;\r
-                       }\r
-               }\r
-               \r
-               /* determine the triangle strip order */\r
-               numIndexes = (ds->numVerts - 2) * 3;\r
-               if( numIndexes > MAX_INDEXES )\r
-                       Error( "MAX_INDEXES exceeded for surface (%d > %d) (%d verts)", numIndexes, MAX_INDEXES, ds->numVerts );\r
-               \r
-               /* try all possible orderings of the points looking for a non-degenerate strip order */\r
-               for( r = 0; r < ds->numVerts; r++ )\r
-               {\r
-                       /* set rotation */\r
-                       rotate = (r + least) % ds->numVerts;\r
-                       \r
-                       /* walk the winding in both directions */\r
-                       for( ni = 0, i = 0; i < ds->numVerts - 2 - i; i++ )\r
-                       {\r
-                               /* make indexes */\r
-                               a = (ds->numVerts - 1 - i + rotate) % ds->numVerts;\r
-                               b = (i + rotate ) % ds->numVerts;\r
-                               c = (ds->numVerts - 2 - i + rotate) % ds->numVerts;\r
-                               \r
-                               /* test this triangle */\r
-                               if( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) )\r
-                                       break;\r
-                               indexes[ ni++ ] = a;\r
-                               indexes[ ni++ ] = b;\r
-                               indexes[ ni++ ] = c;\r
-                               \r
-                               /* handle end case */\r
-                               if( i + 1 != ds->numVerts - 1 - i )\r
-                               {\r
-                                       /* make indexes */\r
-                                       a = (ds->numVerts - 2 - i + rotate ) % ds->numVerts;\r
-                                       b = (i + rotate ) % ds->numVerts;\r
-                                       c = (i + 1 + rotate ) % ds->numVerts;\r
-                                       \r
-                                       /* test triangle */\r
-                                       if( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) )\r
-                                               break;\r
-                                       indexes[ ni++ ] = a;\r
-                                       indexes[ ni++ ] = b;\r
-                                       indexes[ ni++ ] = c;\r
-                               }\r
-                       }\r
-                       \r
-                       /* valid strip? */\r
-                       if( ni == numIndexes )\r
-                               break;\r
-               }\r
-               \r
-               /* if any triangle in the strip is degenerate, render from a centered fan point instead */\r
-               if( ni < numIndexes )\r
-               {\r
-                       FanFaceSurface( ds );\r
-                       return;\r
-               }\r
-       }\r
-       \r
-       /* copy strip triangle indexes */\r
-       ds->numIndexes = numIndexes;\r
-       ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );\r
-       memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );\r
-       \r
-       /* add to count */\r
-       numStripSurfaces++;\r
-       \r
-       /* classify it */\r
-       ClassifySurfaces( 1, ds );\r
-}\r
-\r
-\r
-\r
-/*\r
-MakeEntityMetaTriangles()\r
-builds meta triangles from brush faces (tristrips and fans)\r
-*/\r
-\r
-void MakeEntityMetaTriangles( entity_t *e )\r
-{\r
-       int                                     i, f, fOld, start;\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- MakeEntityMetaTriangles ---\n" );\r
-       \r
-       /* init pacifier */\r
-       fOld = -1;\r
-       start = I_FloatTime();\r
-       \r
-       /* walk the list of surfaces in the entity */\r
-       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )\r
-       {\r
-               /* print pacifier */\r
-               f = 10 * (i - e->firstDrawSurf) / (numMapDrawSurfs - e->firstDrawSurf);\r
-               if( f != fOld )\r
-               {\r
-                       fOld = f;\r
-                       Sys_FPrintf( SYS_VRB, "%d...", f );\r
-               }\r
-               \r
-               /* get surface */\r
-               ds = &mapDrawSurfs[ i ];\r
-               if( ds->numVerts <= 0 )\r
-                       continue;\r
-               \r
-               /* ignore autosprite surfaces */\r
-               if( ds->shaderInfo->autosprite )\r
-                       continue;\r
-               \r
-               /* meta this surface? */\r
-               if( meta == qfalse && ds->shaderInfo->forceMeta == qfalse )\r
-                       continue;\r
-               \r
-               /* switch on type */\r
-               switch( ds->type )\r
-               {\r
-                       case SURFACE_FACE:\r
-                       case SURFACE_DECAL:\r
-                               StripFaceSurface( ds );\r
-                               SurfaceToMetaTriangles( ds );\r
-                               break;\r
-                       \r
-                       case SURFACE_PATCH:\r
-                               TriangulatePatchSurface( ds );\r
-                               break;\r
-                       \r
-                       case SURFACE_TRIANGLES:\r
-                               break;\r
-                       \r
-                       case SURFACE_FORCED_META:\r
-                       case SURFACE_META:\r
-                               SurfaceToMetaTriangles( ds );\r
-                               break;\r
-                       \r
-                       default:\r
-                               break;\r
-               }\r
-       }\r
-       \r
-       /* print time */\r
-       if( (numMapDrawSurfs - e->firstDrawSurf) )\r
-               Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );\r
-       \r
-       /* emit some stats */\r
-       Sys_FPrintf( SYS_VRB, "%9d total meta surfaces\n", numMetaSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d stripped surfaces\n", numStripSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d fanned surfaces\n", numFanSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d patch meta surfaces\n", numPatchMetaSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d meta verts\n", numMetaVerts );\r
-       Sys_FPrintf( SYS_VRB, "%9d meta triangles\n", numMetaTriangles );\r
-       \r
-       /* tidy things up */\r
-       TidyEntitySurfaces( e );\r
-}\r
-\r
-\r
-\r
-/*\r
-PointTriangleIntersect()\r
-assuming that all points lie in plane, determine if pt\r
-is inside the triangle abc\r
-code originally (c) 2001 softSurfer (www.softsurfer.com)\r
-*/\r
-\r
-#define MIN_OUTSIDE_EPSILON            -0.01f\r
-#define MAX_OUTSIDE_EPSILON            1.01f\r
-\r
-static qboolean PointTriangleIntersect( vec3_t pt, vec4_t plane, vec3_t a, vec3_t b, vec3_t c, vec3_t bary )\r
-{\r
-       vec3_t  u, v, w;\r
-       float   uu, uv, vv, wu, wv, d;\r
-       \r
-       \r
-       /* make vectors */\r
-       VectorSubtract( b, a, u );\r
-       VectorSubtract( c, a, v );\r
-       VectorSubtract( pt, a, w );\r
-       \r
-       /* more setup */\r
-       uu = DotProduct( u, u );\r
-       uv = DotProduct( u, v );\r
-       vv = DotProduct( v, v );\r
-       wu = DotProduct( w, u );\r
-       wv = DotProduct( w, v );\r
-       d = uv * uv - uu * vv;\r
-       \r
-       /* calculate barycentric coordinates */\r
-       bary[ 1 ] = (uv * wv - vv * wu) / d;\r
-       if( bary[ 1 ] < MIN_OUTSIDE_EPSILON || bary[ 1 ] > MAX_OUTSIDE_EPSILON )\r
-               return qfalse;\r
-       bary[ 2 ] = (uv * wv - uu * wv) / d;\r
-       if( bary[ 2 ] < MIN_OUTSIDE_EPSILON || bary[ 2 ] > MAX_OUTSIDE_EPSILON )\r
-               return qfalse;\r
-       bary[ 0 ] = 1.0f - (bary[ 1 ] + bary[ 2 ]);\r
-       \r
-       /* point is in triangle */\r
-       return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-CreateEdge()\r
-sets up an edge structure from a plane and 2 points that the edge ab falls lies in\r
-*/\r
-\r
-typedef struct edge_s\r
-{\r
-       vec3_t  origin, edge;\r
-       vec_t   length, kingpinLength;\r
-       int             kingpin;\r
-       vec4_t  plane;\r
-}\r
-edge_t;\r
-\r
-void CreateEdge( vec4_t plane, vec3_t a, vec3_t b, edge_t *edge )\r
-{\r
-       /* copy edge origin */\r
-       VectorCopy( a, edge->origin );\r
-       \r
-       /* create vector aligned with winding direction of edge */\r
-       VectorSubtract( b, a, edge->edge );\r
-       \r
-       if( fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 1 ] ) && fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 2 ] ) )\r
-               edge->kingpin = 0;\r
-       else if( fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 0 ] ) && fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 2 ] ) )\r
-               edge->kingpin = 1;\r
-       else\r
-               edge->kingpin = 2;\r
-       edge->kingpinLength = edge->edge[ edge->kingpin ];\r
-       \r
-       VectorNormalize( edge->edge, edge->edge );\r
-       edge->edge[ 3 ] = DotProduct( a, edge->edge );\r
-       edge->length = DotProduct( b, edge->edge ) - edge->edge[ 3 ];\r
-       \r
-       /* create perpendicular plane that edge lies in */\r
-       CrossProduct( plane, edge->edge, edge->plane );\r
-       edge->plane[ 3 ] = DotProduct( a, edge->plane );\r
-}\r
-\r
-\r
-\r
-/*\r
-FixMetaTJunctions()\r
-fixes t-junctions on meta triangles\r
-*/\r
-\r
-#define TJ_PLANE_EPSILON       (1.0f / 8.0f)\r
-#define TJ_EDGE_EPSILON                (1.0f / 8.0f)\r
-#define TJ_POINT_EPSILON       (1.0f / 8.0f)\r
-\r
-void FixMetaTJunctions( void )\r
-{\r
-       int                             i, j, k, f, fOld, start, vertIndex, triIndex, numTJuncs;\r
-       metaTriangle_t  *tri, *newTri;\r
-       shaderInfo_t    *si;\r
-       bspDrawVert_t   *a, *b, *c, junc;\r
-       float                   dist, amount;\r
-       vec3_t                  pt;\r
-       vec4_t                  plane;\r
-       edge_t                  edges[ 3 ];\r
-       \r
-       \r
-       /* this code is crap; revisit later */\r
-       return;\r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- FixMetaTJunctions ---\n" );\r
-       \r
-       /* init pacifier */\r
-       fOld = -1;\r
-       start = I_FloatTime();\r
-       \r
-       /* walk triangle list */\r
-       numTJuncs = 0;\r
-       for( i = 0; i < numMetaTriangles; i++ )\r
-       {\r
-               /* get triangle */\r
-               tri = &metaTriangles[ i ];\r
-               \r
-               /* print pacifier */\r
-               f = 10 * i / numMetaTriangles;\r
-               if( f != fOld )\r
-               {\r
-                       fOld = f;\r
-                       Sys_FPrintf( SYS_VRB, "%d...", f );\r
-               }\r
-               \r
-               /* attempt to early out */\r
-               si = tri->si;\r
-               if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc )\r
-                       continue;\r
-               \r
-               /* calculate planes */\r
-               VectorCopy( tri->plane, plane );\r
-               plane[ 3 ] = tri->plane[ 3 ];\r
-               CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );\r
-               CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );\r
-               CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );\r
-               \r
-               /* walk meta vert list */\r
-               for( j = 0; j < numMetaVerts; j++ )\r
-               {\r
-                       /* get vert */\r
-                       VectorCopy( metaVerts[ j ].xyz, pt );\r
-\r
-                       /* debug code: darken verts */\r
-                       if( i == 0 )\r
-                               VectorSet( metaVerts[ j ].color[ 0 ], 8, 8, 8 );\r
-                       \r
-                       /* determine if point lies in the triangle's plane */\r
-                       dist = DotProduct( pt, plane ) - plane[ 3 ];\r
-                       if( fabs( dist ) > TJ_PLANE_EPSILON )\r
-                               continue;\r
-                       \r
-                       /* skip this point if it already exists in the triangle */\r
-                       for( k = 0; k < 3; k++ )\r
-                       {\r
-                               if( fabs( pt[ 0 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 0 ] ) <= TJ_POINT_EPSILON &&\r
-                                       fabs( pt[ 1 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 1 ] ) <= TJ_POINT_EPSILON &&\r
-                                       fabs( pt[ 2 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 2 ] ) <= TJ_POINT_EPSILON )\r
-                                       break;\r
-                       }\r
-                       if( k < 3 )\r
-                               continue;\r
-                       \r
-                       /* walk edges */\r
-                       for( k = 0; k < 3; k++ )\r
-                       {\r
-                               /* ignore bogus edges */\r
-                               if( fabs( edges[ k ].kingpinLength ) < TJ_EDGE_EPSILON )\r
-                                       continue;\r
-                               \r
-                               /* determine if point lies on the edge */\r
-                               dist = DotProduct( pt, edges[ k ].plane ) - edges[ k ].plane[ 3 ];\r
-                               if( fabs( dist ) > TJ_EDGE_EPSILON )\r
-                                       continue;\r
-                               \r
-                               /* determine how far along the edge the point lies */\r
-                               amount = (pt[ edges[ k ].kingpin ] - edges[ k ].origin[ edges[ k ].kingpin ]) / edges[ k ].kingpinLength;\r
-                               if( amount <= 0.0f || amount >= 1.0f )\r
-                                       continue;\r
-                               \r
-                               #if 0\r
-                               dist = DotProduct( pt, edges[ k ].edge ) - edges[ k ].edge[ 3 ];\r
-                               if( dist <= -0.0f || dist >= edges[ k ].length )\r
-                                       continue;\r
-                               amount = dist / edges[ k ].length;\r
-                               #endif\r
-                               \r
-                               /* debug code: brighten this point */\r
-                               //%     metaVerts[ j ].color[ 0 ][ 0 ] += 5;\r
-                               //%     metaVerts[ j ].color[ 0 ][ 1 ] += 4;\r
-                               VectorSet( metaVerts[ tri->indexes[ k ] ].color[ 0 ], 255, 204, 0 );\r
-                               VectorSet( metaVerts[ tri->indexes[ (k + 1) % 3 ] ].color[ 0 ], 255, 204, 0 );\r
-                               \r
-\r
-                               /* the edge opposite the zero-weighted vertex was hit, so use that as an amount */\r
-                               a = &metaVerts[ tri->indexes[ k % 3 ] ];\r
-                               b = &metaVerts[ tri->indexes[ (k + 1) % 3 ] ];\r
-                               c = &metaVerts[ tri->indexes[ (k + 2) % 3 ] ];\r
-                               \r
-                               /* make new vert */\r
-                               LerpDrawVertAmount( a, b, amount, &junc );\r
-                               VectorCopy( pt, junc.xyz );\r
-                               \r
-                               /* compare against existing verts */\r
-                               if( VectorCompare( junc.xyz, a->xyz ) || VectorCompare( junc.xyz, b->xyz ) || VectorCompare( junc.xyz, c->xyz ) )\r
-                                       continue;\r
-                               \r
-                               /* see if we can just re-use the existing vert */\r
-                               if( !memcmp( &metaVerts[ j ], &junc, sizeof( junc ) ) )\r
-                                       vertIndex = j;\r
-                               else\r
-                               {\r
-                                       /* find new vertex (note: a and b are invalid pointers after this) */\r
-                                       firstSearchMetaVert = numMetaVerts;\r
-                                       vertIndex = FindMetaVertex( &junc );\r
-                                       if( vertIndex < 0 )\r
-                                               continue;\r
-                               }\r
-                                               \r
-                               /* make new triangle */\r
-                               triIndex = AddMetaTriangle();\r
-                               if( triIndex < 0 )\r
-                                       continue;\r
-                               \r
-                               /* get triangles */\r
-                               tri = &metaTriangles[ i ];\r
-                               newTri = &metaTriangles[ triIndex ];\r
-                               \r
-                               /* copy the triangle */\r
-                               memcpy( newTri, tri, sizeof( *tri ) );\r
-                               \r
-                               /* fix verts */\r
-                               tri->indexes[ (k + 1) % 3 ] = vertIndex;\r
-                               newTri->indexes[ k ] = vertIndex;\r
-                               \r
-                               /* recalculate edges */\r
-                               CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );\r
-                               CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );\r
-                               CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );\r
-                               \r
-                               /* debug code */\r
-                               metaVerts[ vertIndex ].color[ 0 ][ 0 ] = 255;\r
-                               metaVerts[ vertIndex ].color[ 0 ][ 1 ] = 204;\r
-                               metaVerts[ vertIndex ].color[ 0 ][ 2 ] = 0;\r
-                               \r
-                               /* add to counter and end processing of this vert */\r
-                               numTJuncs++;\r
-                               break;\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* print time */\r
-       Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );\r
-       \r
-       /* emit some stats */\r
-       Sys_FPrintf( SYS_VRB, "%9d T-junctions added\n", numTJuncs );\r
-}\r
-\r
-\r
-\r
-/*\r
-SmoothMetaTriangles()\r
-averages coincident vertex normals in the meta triangles\r
-*/\r
-\r
-#define MAX_SAMPLES                            256\r
-#define THETA_EPSILON                  0.000001\r
-#define EQUAL_NORMAL_EPSILON   0.01\r
-\r
-void SmoothMetaTriangles( void )\r
-{\r
-       int                             i, j, k, f, fOld, start, cs, numVerts, numVotes, numSmoothed;\r
-       float                   shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;\r
-       metaTriangle_t  *tri;\r
-       float                   *shadeAngles;\r
-       byte                    *smoothed;\r
-       vec3_t                  average, diff;\r
-       int                             indexes[ MAX_SAMPLES ];\r
-       vec3_t                  votes[ MAX_SAMPLES ];\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- SmoothMetaTriangles ---\n" );\r
-       \r
-       /* allocate shade angle table */\r
-       shadeAngles = safe_malloc( numMetaVerts * sizeof( float ) );\r
-       memset( shadeAngles, 0, numMetaVerts * sizeof( float ) );\r
-       \r
-       /* allocate smoothed table */\r
-       cs = (numMetaVerts / 8) + 1;\r
-       smoothed = safe_malloc( cs );\r
-       memset( smoothed, 0, cs );\r
-       \r
-       /* set default shade angle */\r
-       defaultShadeAngle = DEG2RAD( npDegrees );\r
-       maxShadeAngle = 0.0f;\r
-       \r
-       /* run through every surface and flag verts belonging to non-lightmapped surfaces\r
-          and set per-vertex smoothing angle */\r
-       for( i = 0, tri = &metaTriangles[ i ]; i < numMetaTriangles; i++, tri++ )\r
-       {\r
-               /* get shader for shade angle */\r
-               if( tri->si->shadeAngleDegrees > 0.0f )\r
-                       shadeAngle = DEG2RAD( tri->si->shadeAngleDegrees );\r
-               else\r
-                       shadeAngle = defaultShadeAngle;\r
-               if( shadeAngle > maxShadeAngle )\r
-                       maxShadeAngle = shadeAngle;\r
-               \r
-               /* flag its verts */\r
-               for( j = 0; j < 3; j++ )\r
-               {\r
-                       shadeAngles[ tri->indexes[ j ] ] = shadeAngle;\r
-                       if( shadeAngle <= 0 )\r
-                               smoothed[ tri->indexes[ j ] >> 3 ] |= (1 << (tri->indexes[ j ] & 7));\r
-               }\r
-       }\r
-       \r
-       /* bail if no surfaces have a shade angle */\r
-       if( maxShadeAngle <= 0 )\r
-       {\r
-               Sys_FPrintf( SYS_VRB, "No smoothing angles specified, aborting\n" );\r
-               free( shadeAngles );\r
-               free( smoothed );\r
-               return;\r
-       }\r
-       \r
-       /* init pacifier */\r
-       fOld = -1;\r
-       start = I_FloatTime();\r
-       \r
-       /* go through the list of vertexes */\r
-       numSmoothed = 0;\r
-       for( i = 0; i < numMetaVerts; i++ )\r
-       {\r
-               /* print pacifier */\r
-               f = 10 * i / numMetaVerts;\r
-               if( f != fOld )\r
-               {\r
-                       fOld = f;\r
-                       Sys_FPrintf( SYS_VRB, "%d...", f );\r
-               }\r
-               \r
-               /* already smoothed? */\r
-               if( smoothed[ i >> 3 ] & (1 << (i & 7)) )\r
-                       continue;\r
-               \r
-               /* clear */\r
-               VectorClear( average );\r
-               numVerts = 0;\r
-               numVotes = 0;\r
-               \r
-               /* build a table of coincident vertexes */\r
-               for( j = i; j < numMetaVerts && numVerts < MAX_SAMPLES; j++ )\r
-               {\r
-                       /* already smoothed? */\r
-                       if( smoothed[ j >> 3 ] & (1 << (j & 7)) )\r
-                               continue;\r
-                       \r
-                       /* test vertexes */\r
-                       if( VectorCompare( metaVerts[ i ].xyz, metaVerts[ j ].xyz ) == qfalse )\r
-                               continue;\r
-                       \r
-                       /* use smallest shade angle */\r
-                       shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);\r
-                       \r
-                       /* check shade angle */\r
-                       dot = DotProduct( metaVerts[ i ].normal, metaVerts[ j ].normal );\r
-                       if( dot > 1.0 )\r
-                               dot = 1.0;\r
-                       else if( dot < -1.0 )\r
-                               dot = -1.0;\r
-                       testAngle = acos( dot ) + THETA_EPSILON;\r
-                       if( testAngle >= shadeAngle )\r
-                               continue;\r
-                       \r
-                       /* add to the list */\r
-                       indexes[ numVerts++ ] = j;\r
-                       \r
-                       /* flag vertex */\r
-                       smoothed[ j >> 3 ] |= (1 << (j & 7));\r
-                       \r
-                       /* see if this normal has already been voted */\r
-                       for( k = 0; k < numVotes; k++ )\r
-                       {\r
-                               VectorSubtract( metaVerts[ j ].normal, votes[ k ], diff );\r
-                               if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&\r
-                                       fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&\r
-                                       fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )\r
-                                       break;\r
-                       }\r
-                       \r
-                       /* add a new vote? */\r
-                       if( k == numVotes && numVotes < MAX_SAMPLES )\r
-                       {\r
-                               VectorAdd( average, metaVerts[ j ].normal, average );\r
-                               VectorCopy( metaVerts[ j ].normal, votes[ numVotes ] );\r
-                               numVotes++;\r
-                       }\r
-               }\r
-               \r
-               /* don't average for less than 2 verts */\r
-               if( numVerts < 2 )\r
-                       continue;\r
-               \r
-               /* average normal */\r
-               if( VectorNormalize( average, average ) > 0 )\r
-               {\r
-                       /* smooth */\r
-                       for( j = 0; j < numVerts; j++ )\r
-                               VectorCopy( average, metaVerts[ indexes[ j ] ].normal );\r
-                       numSmoothed++;\r
-               }\r
-       }\r
-       \r
-       /* free the tables */\r
-       free( shadeAngles );\r
-       free( smoothed );\r
-       \r
-       /* print time */\r
-       Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );\r
-\r
-       /* emit some stats */\r
-       Sys_FPrintf( SYS_VRB, "%9d smoothed vertexes\n", numSmoothed );\r
-}\r
-\r
-\r
-\r
-/*\r
-AddMetaVertToSurface()\r
-adds a drawvert to a surface unless an existing vert matching already exists\r
-returns the index of that vert (or < 0 on failure)\r
-*/\r
-\r
-int AddMetaVertToSurface( mapDrawSurface_t *ds, bspDrawVert_t *dv1, int *coincident )\r
-{\r
-       int                             i;\r
-       bspDrawVert_t   *dv2;\r
-       \r
-       \r
-       /* go through the verts and find a suitable candidate */\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-       {\r
-               /* get test vert */\r
-               dv2 = &ds->verts[ i ];\r
-               \r
-               /* compare xyz and normal */\r
-               if( VectorCompare( dv1->xyz, dv2->xyz ) == qfalse )\r
-                       continue;\r
-               if( VectorCompare( dv1->normal, dv2->normal ) == qfalse )\r
-                       continue;\r
-               \r
-               /* good enough at this point */\r
-               (*coincident)++;\r
-               \r
-               /* compare texture coordinates and color */\r
-               if( dv1->st[ 0 ] != dv2->st[ 0 ] || dv1->st[ 1 ] != dv2->st[ 1 ] )\r
-                       continue;\r
-               if( dv1->color[ 0 ][ 3 ] != dv2->color[ 0 ][ 3 ] )\r
-                       continue;\r
-               \r
-               /* found a winner */\r
-               numMergedVerts++;\r
-               return i;\r
-       }\r
-\r
-       /* overflow check */\r
-       if( ds->numVerts >= maxSurfaceVerts )\r
-               return VERTS_EXCEEDED;\r
-       \r
-       /* made it this far, add the vert and return */\r
-       dv2 = &ds->verts[ ds->numVerts++ ];\r
-       *dv2 = *dv1;\r
-       return (ds->numVerts - 1);\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-AddMetaTriangleToSurface()\r
-attempts to add a metatriangle to a surface\r
-returns the score of the triangle added\r
-*/\r
-\r
-#define AXIS_SCORE                     100000\r
-#define AXIS_MIN                       100000\r
-#define VERT_SCORE                     10000\r
-#define SURFACE_SCORE          1000\r
-#define ST_SCORE                       50\r
-#define ST_SCORE2                      (2 * (ST_SCORE))\r
-\r
-#define ADEQUATE_SCORE         ((AXIS_MIN) + 1 * (VERT_SCORE))\r
-#define GOOD_SCORE                     ((AXIS_MIN) + 2 * (VERT_SCORE) + 4 * (ST_SCORE))\r
-#define PERFECT_SCORE          ((AXIS_MIN) + + 3 * (VERT_SCORE) + (SURFACE_SCORE) + 4 * (ST_SCORE))\r
-\r
-static int AddMetaTriangleToSurface( mapDrawSurface_t *ds, metaTriangle_t *tri, qboolean testAdd )\r
-{\r
-       int                                     i, score, coincident, ai, bi, ci, oldTexRange[ 2 ];\r
-       float                           lmMax;\r
-       vec3_t                          mins, maxs;\r
-       qboolean                        inTexRange, es, et;\r
-       mapDrawSurface_t        old;\r
-       \r
-       \r
-       /* overflow check */\r
-       if( ds->numIndexes >= maxSurfaceIndexes )\r
-               return 0;\r
-       \r
-       /* test the triangle */\r
-       if( ds->entityNum != tri->entityNum )   /* ydnar: added 2002-07-06 */\r
-               return 0;\r
-       if( ds->castShadows != tri->castShadows || ds->recvShadows != tri->recvShadows )\r
-               return 0;\r
-       if( ds->shaderInfo != tri->si || ds->fogNum != tri->fogNum || ds->sampleSize != tri->sampleSize )\r
-               return 0;\r
-       #if 0\r
-               if( !(ds->shaderInfo->compileFlags & C_VERTEXLIT) &&\r
-                       //% VectorCompare( ds->lightmapAxis, tri->lightmapAxis ) == qfalse )\r
-                       DotProduct( ds->lightmapAxis, tri->plane ) < 0.25f )\r
-                       return 0;\r
-       #endif\r
-       \r
-       /* planar surfaces will only merge with triangles in the same plane */\r
-       if( npDegrees == 0.0f && ds->shaderInfo->nonplanar == qfalse && ds->planeNum >= 0 )\r
-       {\r
-               if( VectorCompare( mapplanes[ ds->planeNum ].normal, tri->plane ) == qfalse || mapplanes[ ds->planeNum ].dist != tri->plane[ 3 ] )\r
-                       return 0;\r
-               if( tri->planeNum >= 0 && tri->planeNum != ds->planeNum )\r
-                       return 0;\r
-       }\r
-       \r
-       /* set initial score */\r
-       score = tri->surfaceNum == ds->surfaceNum ? SURFACE_SCORE : 0;\r
-       \r
-       /* score the the dot product of lightmap axis to plane */\r
-       if( (ds->shaderInfo->compileFlags & C_VERTEXLIT) || VectorCompare( ds->lightmapAxis, tri->lightmapAxis ) )\r
-               score += AXIS_SCORE;\r
-       else\r
-               score += AXIS_SCORE * DotProduct( ds->lightmapAxis, tri->plane );\r
-       \r
-       /* preserve old drawsurface if this fails */\r
-       memcpy( &old, ds, sizeof( *ds ) );\r
-       \r
-       /* attempt to add the verts */\r
-       coincident = 0;\r
-       ai = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 0 ] ], &coincident );\r
-       bi = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 1 ] ], &coincident );\r
-       ci = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 2 ] ], &coincident );\r
-       \r
-       /* check vertex underflow */\r
-       if( ai < 0 || bi < 0 || ci < 0 )\r
-       {\r
-               memcpy( ds, &old, sizeof( *ds ) );\r
-               return 0;\r
-       }\r
-       \r
-       /* score coincident vertex count (2003-02-14: changed so this only matters on planar surfaces) */\r
-       score += (coincident * VERT_SCORE);\r
-       \r
-       /* add new vertex bounds to mins/maxs */\r
-       VectorCopy( ds->mins, mins );\r
-       VectorCopy( ds->maxs, maxs );\r
-       AddPointToBounds( metaVerts[ tri->indexes[ 0 ] ].xyz, mins, maxs );\r
-       AddPointToBounds( metaVerts[ tri->indexes[ 1 ] ].xyz, mins, maxs );\r
-       AddPointToBounds( metaVerts[ tri->indexes[ 2 ] ].xyz, mins, maxs );\r
-       \r
-       /* check lightmap bounds overflow (after at least 1 triangle has been added) */\r
-       if( !(ds->shaderInfo->compileFlags & C_VERTEXLIT) &&\r
-               ds->numIndexes > 0 && VectorLength( ds->lightmapAxis ) > 0.0f &&\r
-               (VectorCompare( ds->mins, mins ) == qfalse || VectorCompare( ds->maxs, maxs ) == qfalse) )\r
-       {\r
-               /* set maximum size before lightmap scaling (normally 2032 units) */\r
-               lmMax = (ds->sampleSize * (ds->shaderInfo->lmCustomWidth - 1));\r
-               for( i = 0; i < 3; i++ )\r
-               {\r
-                       if( (maxs[ i ] - mins[ i ]) > lmMax )\r
-                       {\r
-                               memcpy( ds, &old, sizeof( *ds ) );\r
-                               return 0;\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* check texture range overflow */\r
-       oldTexRange[ 0 ] = ds->texRange[ 0 ];\r
-       oldTexRange[ 1 ] = ds->texRange[ 1 ];\r
-       inTexRange = CalcSurfaceTextureRange( ds );\r
-       \r
-       es = (ds->texRange[ 0 ] > oldTexRange[ 0 ]) ? qtrue : qfalse;\r
-       et = (ds->texRange[ 1 ] > oldTexRange[ 1 ]) ? qtrue : qfalse;\r
-       \r
-       if( inTexRange == qfalse && ds->numIndexes > 0 )\r
-       {\r
-               memcpy( ds, &old, sizeof( *ds ) );\r
-               return UNSUITABLE_TRIANGLE;\r
-       }\r
-       \r
-       /* score texture range */\r
-       if( ds->texRange[ 0 ] <= oldTexRange[ 0 ] )\r
-               score += ST_SCORE2;\r
-       else if( ds->texRange[ 0 ] > oldTexRange[ 0 ] && oldTexRange[ 1 ] > oldTexRange[ 0 ] )\r
-               score += ST_SCORE;\r
-       \r
-       if( ds->texRange[ 1 ] <= oldTexRange[ 1 ] )\r
-               score += ST_SCORE2;\r
-       else if( ds->texRange[ 1 ] > oldTexRange[ 1 ] && oldTexRange[ 0 ] > oldTexRange[ 1 ] )\r
-               score += ST_SCORE;\r
-       \r
-       \r
-       /* go through the indexes and try to find an existing triangle that matches abc */\r
-       for( i = 0; i < ds->numIndexes; i += 3 )\r
-       {\r
-               /* 2002-03-11 (birthday!): rotate the triangle 3x to find an existing triangle */\r
-               if( (ai == ds->indexes[ i ] && bi == ds->indexes[ i + 1 ] && ci == ds->indexes[ i + 2 ]) ||\r
-                       (bi == ds->indexes[ i ] && ci == ds->indexes[ i + 1 ] && ai == ds->indexes[ i + 2 ]) ||\r
-                       (ci == ds->indexes[ i ] && ai == ds->indexes[ i + 1 ] && bi == ds->indexes[ i + 2 ]) )\r
-               {\r
-                       /* triangle already present */\r
-                       memcpy( ds, &old, sizeof( *ds ) );\r
-                       tri->si = NULL;\r
-                       return 0;\r
-               }\r
-               \r
-               /* rotate the triangle 3x to find an inverse triangle (error case) */\r
-               if( (ai == ds->indexes[ i ] && bi == ds->indexes[ i + 2 ] && ci == ds->indexes[ i + 1 ]) ||\r
-                       (bi == ds->indexes[ i ] && ci == ds->indexes[ i + 2 ] && ai == ds->indexes[ i + 1 ]) ||\r
-                       (ci == ds->indexes[ i ] && ai == ds->indexes[ i + 2 ] && bi == ds->indexes[ i + 1 ]) )\r
-               {\r
-                       /* warn about it */\r
-                       Sys_Printf( "WARNING: Flipped triangle: (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f)\n",\r
-                               ds->verts[ ai ].xyz[ 0 ], ds->verts[ ai ].xyz[ 1 ], ds->verts[ ai ].xyz[ 2 ],\r
-                               ds->verts[ bi ].xyz[ 0 ], ds->verts[ bi ].xyz[ 1 ], ds->verts[ bi ].xyz[ 2 ],\r
-                               ds->verts[ ci ].xyz[ 0 ], ds->verts[ ci ].xyz[ 1 ], ds->verts[ ci ].xyz[ 2 ] );\r
-                       \r
-                       /* reverse triangle already present */\r
-                       memcpy( ds, &old, sizeof( *ds ) );\r
-                       tri->si = NULL;\r
-                       return 0;\r
-               }\r
-       }\r
-       \r
-       /* add the triangle indexes */\r
-       if( ds->numIndexes < maxSurfaceIndexes )\r
-               ds->indexes[ ds->numIndexes++ ] = ai;\r
-       if( ds->numIndexes < maxSurfaceIndexes )\r
-               ds->indexes[ ds->numIndexes++ ] = bi;\r
-       if( ds->numIndexes < maxSurfaceIndexes )\r
-               ds->indexes[ ds->numIndexes++ ] = ci;\r
-       \r
-       /* check index overflow */\r
-       if( ds->numIndexes >= maxSurfaceIndexes  )\r
-       {\r
-               memcpy( ds, &old, sizeof( *ds ) );\r
-               return 0;\r
-       }\r
-       \r
-       /* sanity check the indexes */\r
-       if( ds->numIndexes >= 3 &&\r
-               (ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 2 ] ||\r
-               ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 1 ] ||\r
-               ds->indexes[ ds->numIndexes - 2 ] == ds->indexes[ ds->numIndexes - 1 ]) )\r
-               Sys_Printf( "DEG:%d! ", ds->numVerts );\r
-       \r
-       /* testing only? */\r
-       if( testAdd )\r
-               memcpy( ds, &old, sizeof( *ds ) );\r
-       else\r
-       {\r
-               /* copy bounds back to surface */\r
-               VectorCopy( mins, ds->mins );\r
-               VectorCopy( maxs, ds->maxs );\r
-               \r
-               /* mark triangle as used */\r
-               tri->si = NULL;\r
-       }\r
-       \r
-       /* add a side reference */\r
-       ds->sideRef = AllocSideRef( tri->side, ds->sideRef );\r
-       \r
-       /* return to sender */\r
-       return score;\r
-}\r
-\r
-\r
-\r
-/*\r
-MetaTrianglesToSurface()\r
-creates map drawsurface(s) from the list of possibles\r
-*/\r
-\r
-static void MetaTrianglesToSurface( int numPossibles, metaTriangle_t *possibles, int *fOld, int *numAdded )\r
-{\r
-       int                                     i, j, f, best, score, bestScore;\r
-       metaTriangle_t          *seed, *test;\r
-       mapDrawSurface_t        *ds;\r
-       bspDrawVert_t           *verts;\r
-       int                                     *indexes;\r
-       qboolean                        added;\r
-       \r
-       \r
-       /* allocate arrays */\r
-       verts = safe_malloc( sizeof( *verts ) * maxSurfaceVerts );\r
-       indexes = safe_malloc( sizeof( *indexes ) * maxSurfaceIndexes );\r
-       \r
-       /* walk the list of triangles */\r
-       for( i = 0, seed = possibles; i < numPossibles; i++, seed++ )\r
-       {\r
-               /* skip this triangle if it has already been merged */\r
-               if( seed->si == NULL )\r
-                       continue;\r
-               \r
-               /* -----------------------------------------------------------------\r
-                  initial drawsurf construction\r
-                  ----------------------------------------------------------------- */\r
-               \r
-               /* start a new drawsurface */\r
-               ds = AllocDrawSurface( SURFACE_META );\r
-               ds->entityNum = seed->entityNum;\r
-               ds->surfaceNum = seed->surfaceNum;\r
-               ds->castShadows = seed->castShadows;\r
-               ds->recvShadows = seed->recvShadows;\r
-               \r
-               ds->shaderInfo = seed->si;\r
-               ds->planeNum = seed->planeNum;\r
-               ds->fogNum = seed->fogNum;\r
-               ds->sampleSize = seed->sampleSize;\r
-               ds->verts = verts;\r
-               ds->indexes = indexes;\r
-               VectorCopy( seed->lightmapAxis, ds->lightmapAxis );\r
-               ds->sideRef = AllocSideRef( seed->side, NULL );\r
-               \r
-               ClearBounds( ds->mins, ds->maxs );\r
-               \r
-               /* clear verts/indexes */\r
-               memset( verts, 0, sizeof( verts ) );\r
-               memset( indexes, 0, sizeof( indexes ) );\r
-               \r
-               /* add the first triangle */\r
-               if( AddMetaTriangleToSurface( ds, seed, qfalse ) )\r
-                       (*numAdded)++;\r
-               \r
-               /* -----------------------------------------------------------------\r
-                  add triangles\r
-                  ----------------------------------------------------------------- */\r
-               \r
-               /* progressively walk the list until no more triangles can be added */\r
-               added = qtrue;\r
-               while( added )\r
-               {\r
-                       /* print pacifier */\r
-                       f = 10 * *numAdded / numMetaTriangles;\r
-                       if( f > *fOld )\r
-                       {\r
-                               *fOld = f;\r
-                               Sys_FPrintf( SYS_VRB, "%d...", f );\r
-                       }\r
-                       \r
-                       /* reset best score */\r
-                       best = -1;\r
-                       bestScore = 0;\r
-                       added = qfalse;\r
-                       \r
-                       /* walk the list of possible candidates for merging */\r
-                       for( j = i + 1, test = &possibles[ j ]; j < numPossibles; j++, test++ )\r
-                       {\r
-                               /* score this triangle */\r
-                               score = AddMetaTriangleToSurface( ds, test, qtrue );\r
-                               if( score > bestScore )\r
-                               {\r
-                                       best = j;\r
-                                       bestScore = score;\r
-                                       \r
-                                       /* if we have a score over a certain threshold, just use it */\r
-                                       if( bestScore >= GOOD_SCORE )\r
-                                       {\r
-                                               if( AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse ) )\r
-                                                       (*numAdded)++;\r
-                                               \r
-                                               /* reset */\r
-                                               best = -1;\r
-                                               bestScore = 0;\r
-                                               added = qtrue;\r
-                                       }\r
-                               }\r
-                       }\r
-                       \r
-                       /* add best candidate */\r
-                       if( best >= 0 && bestScore > ADEQUATE_SCORE )\r
-                       {\r
-                               if( AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse ) )\r
-                                       (*numAdded)++;\r
-                               \r
-                               /* reset */\r
-                               added = qtrue;\r
-                       }\r
-               }\r
-               \r
-               /* copy the verts and indexes to the new surface */\r
-               ds->verts = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );\r
-               memcpy( ds->verts, verts, ds->numVerts * sizeof( bspDrawVert_t ) );\r
-               ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );\r
-               memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );\r
-               \r
-               /* classify the surface */\r
-               ClassifySurfaces( 1, ds );\r
-               \r
-               /* add to count */\r
-               numMergedSurfaces++;\r
-       }\r
-       \r
-       /* free arrays */\r
-       free( verts );\r
-       free( indexes );\r
-}\r
-\r
-\r
-\r
-/*\r
-CompareMetaTriangles()\r
-compare function for qsort()\r
-*/\r
-\r
-static int CompareMetaTriangles( const void *a, const void *b )\r
-{\r
-       int             i, j, av, bv;\r
-       vec3_t  aMins, bMins;\r
-       \r
-       \r
-       /* shader first */\r
-       if( ((metaTriangle_t*) a)->si < ((metaTriangle_t*) b)->si )\r
-               return 1;\r
-       else if( ((metaTriangle_t*) a)->si > ((metaTriangle_t*) b)->si )\r
-               return -1;\r
-       \r
-       /* then fog */\r
-       else if( ((metaTriangle_t*) a)->fogNum < ((metaTriangle_t*) b)->fogNum )\r
-               return 1;\r
-       else if( ((metaTriangle_t*) a)->fogNum > ((metaTriangle_t*) b)->fogNum )\r
-               return -1;\r
-       \r
-       /* then position in world */\r
-       else\r
-       {\r
-               /* find mins */\r
-               VectorSet( aMins, 999999, 999999, 999999 );\r
-               VectorSet( bMins, 999999, 999999, 999999 );\r
-               for( i = 0; i < 3; i++ )\r
-               {\r
-                       av = ((metaTriangle_t*) a)->indexes[ i ];\r
-                       bv = ((metaTriangle_t*) b)->indexes[ i ];\r
-                       for( j = 0; j < 3; j++ )\r
-                       {\r
-                               if( metaVerts[ av ].xyz[ j ] < aMins[ j ] )\r
-                                       aMins[ j ] = metaVerts[ av ].xyz[ j ];\r
-                               if( metaVerts[ bv ].xyz[ j ] < bMins[ j ] )\r
-                                       bMins[ j ] = metaVerts[ bv ].xyz[ j ];\r
-                       }\r
-               }\r
-               \r
-               /* test it */\r
-               for( i = 0; i < 3; i++ )\r
-               {\r
-                       if( aMins[ i ] < bMins[ i ] )\r
-                               return 1;\r
-                       else if( aMins[ i ] > bMins[ i ] )\r
-                               return -1;\r
-               }\r
-       }\r
-       \r
-       /* functionally equivalent */\r
-       return 0;\r
-}\r
-\r
-\r
-\r
-/*\r
-MergeMetaTriangles()\r
-merges meta triangles into drawsurfaces\r
-*/\r
-\r
-void MergeMetaTriangles( void )\r
-{\r
-       int                                     i, j, fOld, start, numAdded;\r
-       metaTriangle_t          *head, *end;\r
-       \r
-       \r
-       /* only do this if there are meta triangles */\r
-       if( numMetaTriangles <= 0 )\r
-               return;\r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- MergeMetaTriangles ---\n" );\r
-       \r
-       /* sort the triangles by shader major, fognum minor */\r
-       qsort( metaTriangles, numMetaTriangles, sizeof( metaTriangle_t ), CompareMetaTriangles );\r
-\r
-       /* init pacifier */\r
-       fOld = -1;\r
-       start = I_FloatTime();\r
-       numAdded = 0;\r
-       \r
-       /* merge */\r
-       for( i = 0; i < numMetaTriangles; i = j )\r
-       {\r
-               /* get head of list */\r
-               head = &metaTriangles[ i ];\r
-               \r
-               /* find end */\r
-               for( j = i + 1; j < numMetaTriangles; j++ )\r
-               {\r
-                       /* get end of list */\r
-                       end = &metaTriangles[ j ];\r
-                       if( head->si != end->si || head->fogNum != end->fogNum )\r
-                               break;\r
-               }\r
-               \r
-               /* try to merge this list of possible merge candidates */\r
-               MetaTrianglesToSurface( (j - i), head, &fOld, &numAdded );\r
-       }\r
-       \r
-       /* clear meta triangle list */\r
-       ClearMetaTriangles();\r
-       \r
-       /* print time */\r
-       if( i )\r
-               Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );\r
-       \r
-       /* emit some stats */\r
-       Sys_FPrintf( SYS_VRB, "%9d surfaces merged\n", numMergedSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d vertexes merged\n", numMergedVerts );\r
-}\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+----------------------------------------------------------------------------------
+
+This code has been altered significantly from its original form, to support
+several games based on the Quake III Arena engine, in the form of "Q3Map2."
+
+------------------------------------------------------------------------------- */
+
+
+
+/* marker */
+#define SURFACE_META_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+#define LIGHTMAP_EXCEEDED      -1
+#define S_EXCEEDED                     -2
+#define T_EXCEEDED                     -3
+#define ST_EXCEEDED                    -4
+#define UNSUITABLE_TRIANGLE    -10
+#define VERTS_EXCEEDED         -1000
+#define INDEXES_EXCEEDED       -2000
+
+#define GROW_META_VERTS                1024
+#define GROW_META_TRIANGLES    1024
+
+static int                                     numMetaSurfaces, numPatchMetaSurfaces;
+
+static int                                     maxMetaVerts = 0;
+static int                                     numMetaVerts = 0;
+static int                                     firstSearchMetaVert = 0;
+static bspDrawVert_t           *metaVerts = NULL;
+
+static int                                     maxMetaTriangles = 0;
+static int                                     numMetaTriangles = 0;
+static metaTriangle_t          *metaTriangles = NULL;
+
+
+
+/*
+ClearMetaVertexes()
+called before staring a new entity to clear out the triangle list
+*/
+
+void ClearMetaTriangles( void )
+{
+       numMetaVerts = 0;
+       numMetaTriangles = 0;
+}
+
+
+
+/*
+FindMetaVertex()
+finds a matching metavertex in the global list, returning its index
+*/
+
+static int FindMetaVertex( bspDrawVert_t *src )
+{
+       int                     i;
+       bspDrawVert_t   *v, *temp;
+       
+       
+       /* try to find an existing drawvert */
+       for( i = firstSearchMetaVert, v = &metaVerts[ i ]; i < numMetaVerts; i++, v++ )
+       {
+               if( memcmp( src, v, sizeof( bspDrawVert_t ) ) == 0 )
+                       return i;
+       }
+       
+       /* enough space? */
+       if( numMetaVerts >= maxMetaVerts )
+       {
+               /* reallocate more room */
+               maxMetaVerts += GROW_META_VERTS;
+               temp = safe_malloc( maxMetaVerts * sizeof( bspDrawVert_t ) );
+               if( metaVerts != NULL )
+               {
+                       memcpy( temp, metaVerts, numMetaVerts * sizeof( bspDrawVert_t ) );
+                       free( metaVerts );
+               }
+               metaVerts = temp;
+       }
+       
+       /* add the triangle */
+       memcpy( &metaVerts[ numMetaVerts ], src, sizeof( bspDrawVert_t ) );
+       numMetaVerts++;
+       
+       /* return the count */
+       return (numMetaVerts - 1);
+}
+
+
+
+/*
+AddMetaTriangle()
+adds a new meta triangle, allocating more memory if necessary
+*/
+
+static int AddMetaTriangle( void )
+{
+       metaTriangle_t  *temp;
+       
+       
+       /* enough space? */
+       if( numMetaTriangles >= maxMetaTriangles )
+       {
+               /* reallocate more room */
+               maxMetaTriangles += GROW_META_TRIANGLES;
+               temp = safe_malloc( maxMetaTriangles * sizeof( metaTriangle_t ) );
+               if( metaTriangles != NULL )
+               {
+                       memcpy( temp, metaTriangles, numMetaTriangles * sizeof( metaTriangle_t ) );
+                       free( metaTriangles );
+               }
+               metaTriangles = temp;
+       }
+       
+       /* increment and return */
+       numMetaTriangles++;
+       return numMetaTriangles - 1;
+}
+
+
+
+/*
+FindMetaTriangle()
+finds a matching metatriangle in the global list,
+otherwise adds it and returns the index to the metatriangle
+*/
+
+int FindMetaTriangle( metaTriangle_t *src, bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *c, int planeNum )
+{
+       int                             triIndex;
+       vec3_t                  dir;
+
+       
+       
+       /* detect degenerate triangles fixme: do something proper here */
+       VectorSubtract( a->xyz, b->xyz, dir );
+       if( VectorLength( dir ) < 0.125f )
+               return -1;
+       VectorSubtract( b->xyz, c->xyz, dir );
+       if( VectorLength( dir ) < 0.125f )
+               return -1;
+       VectorSubtract( c->xyz, a->xyz, dir );
+       if( VectorLength( dir ) < 0.125f )
+               return -1;
+       
+       /* find plane */
+       if( planeNum >= 0 )
+       {
+               /* because of precision issues with small triangles, try to use the specified plane */
+               src->planeNum = planeNum;
+               VectorCopy( mapplanes[ planeNum ].normal, src->plane );
+               src->plane[ 3 ] = mapplanes[ planeNum ].dist;
+       }
+       else
+       {
+               /* calculate a plane from the triangle's points (and bail if a plane can't be constructed) */
+               src->planeNum = -1;
+               if( PlaneFromPoints( src->plane, a->xyz, b->xyz, c->xyz ) == qfalse )
+                       return -1;
+       }
+       
+       /* ydnar 2002-10-03: repair any bogus normals (busted ase import kludge) */
+       if( VectorLength( a->normal ) <= 0.0f )
+               VectorCopy( src->plane, a->normal );
+       if( VectorLength( b->normal ) <= 0.0f )
+               VectorCopy( src->plane, b->normal );
+       if( VectorLength( c->normal ) <= 0.0f )
+               VectorCopy( src->plane, c->normal );
+       
+       /* ydnar 2002-10-04: set lightmap axis if not already set */
+       if( !(src->si->compileFlags & C_VERTEXLIT) &&
+               src->lightmapAxis[ 0 ] == 0.0f && src->lightmapAxis[ 1 ] == 0.0f && src->lightmapAxis[ 2 ] == 0.0f )
+       {
+               /* the shader can specify an explicit lightmap axis */
+               if( src->si->lightmapAxis[ 0 ] || src->si->lightmapAxis[ 1 ] || src->si->lightmapAxis[ 2 ] )
+                       VectorCopy( src->si->lightmapAxis, src->lightmapAxis );
+               
+               /* new axis-finding code */
+               else
+                       CalcLightmapAxis( src->plane, src->lightmapAxis );
+       }
+       
+       /* fill out the src triangle */
+       src->indexes[ 0 ] = FindMetaVertex( a );
+       src->indexes[ 1 ] = FindMetaVertex( b );
+       src->indexes[ 2 ] = FindMetaVertex( c );
+       
+       /* try to find an existing triangle */
+       #ifdef USE_EXHAUSTIVE_SEARCH
+       {
+               int                             i;
+               metaTriangle_t  *tri;
+               
+               
+               for( i = 0, tri = metaTriangles; i < numMetaTriangles; i++, tri++ )
+               {
+                       if( memcmp( src, tri, sizeof( metaTriangle_t ) ) == 0 )
+                               return i;
+               }
+       }
+       #endif
+       
+       /* get a new triangle */
+       triIndex = AddMetaTriangle();
+       
+       /* add the triangle */
+       memcpy( &metaTriangles[ triIndex ], src, sizeof( metaTriangle_t ) );
+       
+       /* return the triangle index */
+       return triIndex;
+}
+
+
+
+/*
+SurfaceToMetaTriangles()
+converts a classified surface to metatriangles
+*/
+
+static void SurfaceToMetaTriangles( mapDrawSurface_t *ds )
+{
+       int                             i;
+       metaTriangle_t  src;
+       bspDrawVert_t   a, b, c;
+       
+       
+       /* only handle certain types of surfaces */
+       if( ds->type != SURFACE_FACE &&
+               ds->type != SURFACE_META &&
+               ds->type != SURFACE_FORCED_META &&
+               ds->type != SURFACE_DECAL )
+               return;
+       
+       /* speed at the expense of memory */
+       firstSearchMetaVert = numMetaVerts;
+       
+       /* only handle valid surfaces */
+       if( ds->type != SURFACE_BAD && ds->numVerts >= 3 && ds->numIndexes >= 3 )
+       {
+               /* walk the indexes and create triangles */
+               for( i = 0; i < ds->numIndexes; i += 3 )
+               {
+                       /* sanity check the indexes */
+                       if( ds->indexes[ i ] == ds->indexes[ i + 1 ] ||
+                               ds->indexes[ i ] == ds->indexes[ i + 2 ] ||
+                               ds->indexes[ i + 1 ] == ds->indexes[ i + 2 ] )
+                       {
+                               //%     Sys_Printf( "%d! ", ds->numVerts );
+                               continue;
+                       }
+                       
+                       /* build a metatriangle */
+                       src.si = ds->shaderInfo;
+                       src.side = (ds->sideRef != NULL ? ds->sideRef->side : NULL);
+                       src.entityNum = ds->entityNum;
+                       src.surfaceNum = ds->surfaceNum;
+                       src.planeNum = ds->planeNum;
+                       src.castShadows = ds->castShadows;
+                       src.recvShadows = ds->recvShadows;
+                       src.fogNum = ds->fogNum;
+                       src.sampleSize = ds->sampleSize;
+                       VectorCopy( ds->lightmapAxis, src.lightmapAxis );
+                       
+                       /* copy drawverts */
+                       memcpy( &a, &ds->verts[ ds->indexes[ i ] ], sizeof( a ) );
+                       memcpy( &b, &ds->verts[ ds->indexes[ i + 1 ] ], sizeof( b ) );
+                       memcpy( &c, &ds->verts[ ds->indexes[ i + 2 ] ], sizeof( c ) );
+                       FindMetaTriangle( &src, &a, &b, &c, ds->planeNum );
+               }
+               
+               /* add to count */
+               numMetaSurfaces++;
+       }
+       
+       /* clear the surface (free verts and indexes, sets it to SURFACE_BAD) */
+       ClearSurface( ds );
+}
+
+
+
+/*
+TriangulatePatchSurface()
+creates triangles from a patch
+*/
+
+void TriangulatePatchSurface( mapDrawSurface_t *ds )
+{
+       int                                     iterations, x, y, pw[ 5 ], r;
+       mapDrawSurface_t        *dsNew;
+       mesh_t                          src, *subdivided, *mesh;
+       
+       
+       /* try to early out */
+       if( ds->numVerts == 0 || ds->type != SURFACE_PATCH || patchMeta == qfalse )
+               return;
+       
+       /* make a mesh from the drawsurf */ 
+       src.width = ds->patchWidth;
+       src.height = ds->patchHeight;
+       src.verts = ds->verts;
+       //%     subdivided = SubdivideMesh( src, 8, 999 );
+       iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );
+       subdivided = SubdivideMesh2( src, iterations ); //%     ds->maxIterations
+       
+       /* fit it to the curve and remove colinear verts on rows/columns */
+       PutMeshOnCurve( *subdivided );
+       mesh = RemoveLinearMeshColumnsRows( subdivided );
+       FreeMesh( subdivided );
+       //% MakeMeshNormals( mesh );
+       
+       /* make a copy of the drawsurface */
+       dsNew = AllocDrawSurface( SURFACE_META );
+       memcpy( dsNew, ds, sizeof( *ds ) );
+       
+       /* if the patch is nonsolid, then discard it */
+       if( !(ds->shaderInfo->compileFlags & C_SOLID) )
+               ClearSurface( ds );
+       
+       /* set new pointer */
+       ds = dsNew;
+       
+       /* basic transmogrification */
+       ds->type = SURFACE_META;
+       ds->numIndexes = 0;
+       ds->indexes = safe_malloc( mesh->width * mesh->height * 6 * sizeof( int ) );
+       
+       /* copy the verts in */
+       ds->numVerts = (mesh->width * mesh->height);
+       ds->verts = mesh->verts;
+       
+       /* iterate through the mesh quads */
+       for( y = 0; y < (mesh->height - 1); y++ )
+       {
+               for( x = 0; x < (mesh->width - 1); x++ )
+               {
+                       /* set indexes */
+                       pw[ 0 ] = x + (y * mesh->width);
+                       pw[ 1 ] = x + ((y + 1) * mesh->width);
+                       pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
+                       pw[ 3 ] = x + 1 + (y * mesh->width);
+                       pw[ 4 ] = x + (y * mesh->width);        /* same as pw[ 0 ] */
+                       
+                       /* set radix */
+                       r = (x + y) & 1;
+                       
+                       /* make first triangle */
+                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];
+                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 1 ];
+                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];
+                       
+                       /* make second triangle */
+                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 0 ];
+                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 2 ];
+                       ds->indexes[ ds->numIndexes++ ] = pw[ r + 3 ];
+               }
+       }
+       
+       /* free the mesh, but not the verts */
+       free( mesh );
+       
+       /* add to count */
+       numPatchMetaSurfaces++;
+       
+       /* classify it */
+       ClassifySurfaces( 1, ds );
+}
+
+
+
+/*
+FanFaceSurface() - ydnar
+creates a tri-fan from a brush face winding
+loosely based on SurfaceAsTriFan()
+*/
+
+void FanFaceSurface( mapDrawSurface_t *ds )
+{
+       int                             i, j, k, a, b, c, color[ MAX_LIGHTMAPS ][ 4 ];
+       bspDrawVert_t   *verts, *centroid, *dv;
+       double                  iv;
+       
+       
+       /* try to early out */
+       if( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) )
+               return;
+       
+       /* add a new vertex at the beginning of the surface */
+       verts = safe_malloc( (ds->numVerts + 1) * sizeof( bspDrawVert_t ) );
+       memset( verts, 0, sizeof( bspDrawVert_t ) );
+       memcpy( &verts[ 1 ], ds->verts, ds->numVerts * sizeof( bspDrawVert_t ) );
+       free( ds->verts );
+       ds->verts = verts;
+       
+       /* add up the drawverts to create a centroid */
+       centroid = &verts[ 0 ];
+       memset( color, 0,  4 * MAX_LIGHTMAPS * sizeof( int ) );
+       for( i = 1, dv = &verts[ 1 ]; i < (ds->numVerts + 1); i++, dv++ )
+       {
+               VectorAdd( centroid->xyz, dv->xyz, centroid->xyz );
+               VectorAdd( centroid->normal, dv->normal, centroid->normal );
+               for( j = 0; j < 4; j++ )
+               {
+                       for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                               color[ k ][ j ] += dv->color[ k ][ j ];
+                       if( j < 2 )
+                       {
+                               centroid->st[ j ] += dv->st[ j ];
+                               for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                                       centroid->lightmap[ k ][ j ] += dv->lightmap[ k ][ j ];
+                       }
+               }
+       }
+       
+       /* average the centroid */
+       iv = 1.0f / ds->numVerts;
+       VectorScale( centroid->xyz, iv, centroid->xyz );
+       if( VectorNormalize( centroid->normal, centroid->normal ) <= 0 )
+               VectorCopy( verts[ 1 ].normal, centroid->normal );
+       for( j = 0; j < 4; j++ )
+       {
+               for( k = 0; k < MAX_LIGHTMAPS; k++ )
+               {
+                       color[ k ][ j ] /= ds->numVerts;
+                       centroid->color[ k ][ j ] = (color[ k ][ j ] < 255.0f ? color[ k ][ j ] : 255);
+               }
+               if( j < 2 )
+               {
+                       centroid->st[ j ] *= iv;
+                       for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                               centroid->lightmap[ k ][ j ] *= iv;
+               }
+       }
+       
+       /* add to vert count */
+       ds->numVerts++;
+       
+       /* fill indexes in triangle fan order */
+       ds->numIndexes = 0;
+       ds->indexes = safe_malloc( ds->numVerts * 3 * sizeof( int ) );
+       for( i = 1; i < ds->numVerts; i++ )
+       {
+               a = 0;
+               b = i;
+               c = (i + 1) % ds->numVerts;
+               c = c ? c : 1;
+               ds->indexes[ ds->numIndexes++ ] = a;
+               ds->indexes[ ds->numIndexes++ ] = b;
+               ds->indexes[ ds->numIndexes++ ] = c;
+       }
+       
+       /* add to count */
+       numFanSurfaces++;
+
+       /* classify it */
+       ClassifySurfaces( 1, ds );
+}
+
+
+
+/*
+StripFaceSurface() - ydnar
+attempts to create a valid tri-strip w/o degenerate triangles from a brush face winding
+based on SurfaceAsTriStrip()
+*/
+
+#define MAX_INDEXES            1024
+
+void StripFaceSurface( mapDrawSurface_t *ds ) 
+{
+       int                     i, r, least, rotate, numIndexes, ni, a, b, c, indexes[ MAX_INDEXES ];
+       vec_t           *v1, *v2;
+       
+       
+       /* try to early out  */
+       if( !ds->numVerts || (ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL) )
+               return;
+       
+       /* is this a simple triangle? */
+       if( ds->numVerts == 3 )
+       {
+               numIndexes = 3;
+               VectorSet( indexes, 0, 1, 2 );
+       }
+       else
+       {
+               /* ydnar: find smallest coordinate */
+               least = 0;
+               if( ds->shaderInfo != NULL && ds->shaderInfo->autosprite == qfalse )
+               {
+                       for( i = 0; i < ds->numVerts; i++ )
+                       {
+                               /* get points */
+                               v1 = ds->verts[ i ].xyz;
+                               v2 = ds->verts[ least ].xyz;
+                               
+                               /* compare */
+                               if( v1[ 0 ] < v2[ 0 ] ||
+                                       (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] < v2[ 1 ]) ||
+                                       (v1[ 0 ] == v2[ 0 ] && v1[ 1 ] == v2[ 1 ] && v1[ 2 ] < v2[ 2 ]) )
+                                       least = i;
+                       }
+               }
+               
+               /* determine the triangle strip order */
+               numIndexes = (ds->numVerts - 2) * 3;
+               if( numIndexes > MAX_INDEXES )
+                       Error( "MAX_INDEXES exceeded for surface (%d > %d) (%d verts)", numIndexes, MAX_INDEXES, ds->numVerts );
+               
+               /* try all possible orderings of the points looking for a non-degenerate strip order */
+               for( r = 0; r < ds->numVerts; r++ )
+               {
+                       /* set rotation */
+                       rotate = (r + least) % ds->numVerts;
+                       
+                       /* walk the winding in both directions */
+                       for( ni = 0, i = 0; i < ds->numVerts - 2 - i; i++ )
+                       {
+                               /* make indexes */
+                               a = (ds->numVerts - 1 - i + rotate) % ds->numVerts;
+                               b = (i + rotate ) % ds->numVerts;
+                               c = (ds->numVerts - 2 - i + rotate) % ds->numVerts;
+                               
+                               /* test this triangle */
+                               if( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) )
+                                       break;
+                               indexes[ ni++ ] = a;
+                               indexes[ ni++ ] = b;
+                               indexes[ ni++ ] = c;
+                               
+                               /* handle end case */
+                               if( i + 1 != ds->numVerts - 1 - i )
+                               {
+                                       /* make indexes */
+                                       a = (ds->numVerts - 2 - i + rotate ) % ds->numVerts;
+                                       b = (i + rotate ) % ds->numVerts;
+                                       c = (i + 1 + rotate ) % ds->numVerts;
+                                       
+                                       /* test triangle */
+                                       if( ds->numVerts > 4 && IsTriangleDegenerate( ds->verts, a, b, c ) )
+                                               break;
+                                       indexes[ ni++ ] = a;
+                                       indexes[ ni++ ] = b;
+                                       indexes[ ni++ ] = c;
+                               }
+                       }
+                       
+                       /* valid strip? */
+                       if( ni == numIndexes )
+                               break;
+               }
+               
+               /* if any triangle in the strip is degenerate, render from a centered fan point instead */
+               if( ni < numIndexes )
+               {
+                       FanFaceSurface( ds );
+                       return;
+               }
+       }
+       
+       /* copy strip triangle indexes */
+       ds->numIndexes = numIndexes;
+       ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );
+       memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );
+       
+       /* add to count */
+       numStripSurfaces++;
+       
+       /* classify it */
+       ClassifySurfaces( 1, ds );
+}
+
+
+
+/*
+MakeEntityMetaTriangles()
+builds meta triangles from brush faces (tristrips and fans)
+*/
+
+void MakeEntityMetaTriangles( entity_t *e )
+{
+       int                                     i, f, fOld, start;
+       mapDrawSurface_t        *ds;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- MakeEntityMetaTriangles ---\n" );
+       
+       /* init pacifier */
+       fOld = -1;
+       start = I_FloatTime();
+       
+       /* walk the list of surfaces in the entity */
+       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
+       {
+               /* print pacifier */
+               f = 10 * (i - e->firstDrawSurf) / (numMapDrawSurfs - e->firstDrawSurf);
+               if( f != fOld )
+               {
+                       fOld = f;
+                       Sys_FPrintf( SYS_VRB, "%d...", f );
+               }
+               
+               /* get surface */
+               ds = &mapDrawSurfs[ i ];
+               if( ds->numVerts <= 0 )
+                       continue;
+               
+               /* ignore autosprite surfaces */
+               if( ds->shaderInfo->autosprite )
+                       continue;
+               
+               /* meta this surface? */
+               if( meta == qfalse && ds->shaderInfo->forceMeta == qfalse )
+                       continue;
+               
+               /* switch on type */
+               switch( ds->type )
+               {
+                       case SURFACE_FACE:
+                       case SURFACE_DECAL:
+                               StripFaceSurface( ds );
+                               SurfaceToMetaTriangles( ds );
+                               break;
+                       
+                       case SURFACE_PATCH:
+                               TriangulatePatchSurface( ds );
+                               break;
+                       
+                       case SURFACE_TRIANGLES:
+                               break;
+                       
+                       case SURFACE_FORCED_META:
+                       case SURFACE_META:
+                               SurfaceToMetaTriangles( ds );
+                               break;
+                       
+                       default:
+                               break;
+               }
+       }
+       
+       /* print time */
+       if( (numMapDrawSurfs - e->firstDrawSurf) )
+               Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
+       
+       /* emit some stats */
+       Sys_FPrintf( SYS_VRB, "%9d total meta surfaces\n", numMetaSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d stripped surfaces\n", numStripSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d fanned surfaces\n", numFanSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d patch meta surfaces\n", numPatchMetaSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d meta verts\n", numMetaVerts );
+       Sys_FPrintf( SYS_VRB, "%9d meta triangles\n", numMetaTriangles );
+       
+       /* tidy things up */
+       TidyEntitySurfaces( e );
+}
+
+
+
+/*
+PointTriangleIntersect()
+assuming that all points lie in plane, determine if pt
+is inside the triangle abc
+code originally (c) 2001 softSurfer (www.softsurfer.com)
+*/
+
+#define MIN_OUTSIDE_EPSILON            -0.01f
+#define MAX_OUTSIDE_EPSILON            1.01f
+
+static qboolean PointTriangleIntersect( vec3_t pt, vec4_t plane, vec3_t a, vec3_t b, vec3_t c, vec3_t bary )
+{
+       vec3_t  u, v, w;
+       float   uu, uv, vv, wu, wv, d;
+       
+       
+       /* make vectors */
+       VectorSubtract( b, a, u );
+       VectorSubtract( c, a, v );
+       VectorSubtract( pt, a, w );
+       
+       /* more setup */
+       uu = DotProduct( u, u );
+       uv = DotProduct( u, v );
+       vv = DotProduct( v, v );
+       wu = DotProduct( w, u );
+       wv = DotProduct( w, v );
+       d = uv * uv - uu * vv;
+       
+       /* calculate barycentric coordinates */
+       bary[ 1 ] = (uv * wv - vv * wu) / d;
+       if( bary[ 1 ] < MIN_OUTSIDE_EPSILON || bary[ 1 ] > MAX_OUTSIDE_EPSILON )
+               return qfalse;
+       bary[ 2 ] = (uv * wv - uu * wv) / d;
+       if( bary[ 2 ] < MIN_OUTSIDE_EPSILON || bary[ 2 ] > MAX_OUTSIDE_EPSILON )
+               return qfalse;
+       bary[ 0 ] = 1.0f - (bary[ 1 ] + bary[ 2 ]);
+       
+       /* point is in triangle */
+       return qtrue;
+}
+
+
+
+/*
+CreateEdge()
+sets up an edge structure from a plane and 2 points that the edge ab falls lies in
+*/
+
+typedef struct edge_s
+{
+       vec3_t  origin, edge;
+       vec_t   length, kingpinLength;
+       int             kingpin;
+       vec4_t  plane;
+}
+edge_t;
+
+void CreateEdge( vec4_t plane, vec3_t a, vec3_t b, edge_t *edge )
+{
+       /* copy edge origin */
+       VectorCopy( a, edge->origin );
+       
+       /* create vector aligned with winding direction of edge */
+       VectorSubtract( b, a, edge->edge );
+       
+       if( fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 1 ] ) && fabs( edge->edge[ 0 ] ) > fabs( edge->edge[ 2 ] ) )
+               edge->kingpin = 0;
+       else if( fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 0 ] ) && fabs( edge->edge[ 1 ] ) > fabs( edge->edge[ 2 ] ) )
+               edge->kingpin = 1;
+       else
+               edge->kingpin = 2;
+       edge->kingpinLength = edge->edge[ edge->kingpin ];
+       
+       VectorNormalize( edge->edge, edge->edge );
+       edge->edge[ 3 ] = DotProduct( a, edge->edge );
+       edge->length = DotProduct( b, edge->edge ) - edge->edge[ 3 ];
+       
+       /* create perpendicular plane that edge lies in */
+       CrossProduct( plane, edge->edge, edge->plane );
+       edge->plane[ 3 ] = DotProduct( a, edge->plane );
+}
+
+
+
+/*
+FixMetaTJunctions()
+fixes t-junctions on meta triangles
+*/
+
+#define TJ_PLANE_EPSILON       (1.0f / 8.0f)
+#define TJ_EDGE_EPSILON                (1.0f / 8.0f)
+#define TJ_POINT_EPSILON       (1.0f / 8.0f)
+
+void FixMetaTJunctions( void )
+{
+       int                             i, j, k, f, fOld, start, vertIndex, triIndex, numTJuncs;
+       metaTriangle_t  *tri, *newTri;
+       shaderInfo_t    *si;
+       bspDrawVert_t   *a, *b, *c, junc;
+       float                   dist, amount;
+       vec3_t                  pt;
+       vec4_t                  plane;
+       edge_t                  edges[ 3 ];
+       
+       
+       /* this code is crap; revisit later */
+       return;
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- FixMetaTJunctions ---\n" );
+       
+       /* init pacifier */
+       fOld = -1;
+       start = I_FloatTime();
+       
+       /* walk triangle list */
+       numTJuncs = 0;
+       for( i = 0; i < numMetaTriangles; i++ )
+       {
+               /* get triangle */
+               tri = &metaTriangles[ i ];
+               
+               /* print pacifier */
+               f = 10 * i / numMetaTriangles;
+               if( f != fOld )
+               {
+                       fOld = f;
+                       Sys_FPrintf( SYS_VRB, "%d...", f );
+               }
+               
+               /* attempt to early out */
+               si = tri->si;
+               if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc )
+                       continue;
+               
+               /* calculate planes */
+               VectorCopy( tri->plane, plane );
+               plane[ 3 ] = tri->plane[ 3 ];
+               CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );
+               CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );
+               CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );
+               
+               /* walk meta vert list */
+               for( j = 0; j < numMetaVerts; j++ )
+               {
+                       /* get vert */
+                       VectorCopy( metaVerts[ j ].xyz, pt );
+
+                       /* debug code: darken verts */
+                       if( i == 0 )
+                               VectorSet( metaVerts[ j ].color[ 0 ], 8, 8, 8 );
+                       
+                       /* determine if point lies in the triangle's plane */
+                       dist = DotProduct( pt, plane ) - plane[ 3 ];
+                       if( fabs( dist ) > TJ_PLANE_EPSILON )
+                               continue;
+                       
+                       /* skip this point if it already exists in the triangle */
+                       for( k = 0; k < 3; k++ )
+                       {
+                               if( fabs( pt[ 0 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 0 ] ) <= TJ_POINT_EPSILON &&
+                                       fabs( pt[ 1 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 1 ] ) <= TJ_POINT_EPSILON &&
+                                       fabs( pt[ 2 ] - metaVerts[ tri->indexes[ k ] ].xyz[ 2 ] ) <= TJ_POINT_EPSILON )
+                                       break;
+                       }
+                       if( k < 3 )
+                               continue;
+                       
+                       /* walk edges */
+                       for( k = 0; k < 3; k++ )
+                       {
+                               /* ignore bogus edges */
+                               if( fabs( edges[ k ].kingpinLength ) < TJ_EDGE_EPSILON )
+                                       continue;
+                               
+                               /* determine if point lies on the edge */
+                               dist = DotProduct( pt, edges[ k ].plane ) - edges[ k ].plane[ 3 ];
+                               if( fabs( dist ) > TJ_EDGE_EPSILON )
+                                       continue;
+                               
+                               /* determine how far along the edge the point lies */
+                               amount = (pt[ edges[ k ].kingpin ] - edges[ k ].origin[ edges[ k ].kingpin ]) / edges[ k ].kingpinLength;
+                               if( amount <= 0.0f || amount >= 1.0f )
+                                       continue;
+                               
+                               #if 0
+                               dist = DotProduct( pt, edges[ k ].edge ) - edges[ k ].edge[ 3 ];
+                               if( dist <= -0.0f || dist >= edges[ k ].length )
+                                       continue;
+                               amount = dist / edges[ k ].length;
+                               #endif
+                               
+                               /* debug code: brighten this point */
+                               //%     metaVerts[ j ].color[ 0 ][ 0 ] += 5;
+                               //%     metaVerts[ j ].color[ 0 ][ 1 ] += 4;
+                               VectorSet( metaVerts[ tri->indexes[ k ] ].color[ 0 ], 255, 204, 0 );
+                               VectorSet( metaVerts[ tri->indexes[ (k + 1) % 3 ] ].color[ 0 ], 255, 204, 0 );
+                               
+
+                               /* the edge opposite the zero-weighted vertex was hit, so use that as an amount */
+                               a = &metaVerts[ tri->indexes[ k % 3 ] ];
+                               b = &metaVerts[ tri->indexes[ (k + 1) % 3 ] ];
+                               c = &metaVerts[ tri->indexes[ (k + 2) % 3 ] ];
+                               
+                               /* make new vert */
+                               LerpDrawVertAmount( a, b, amount, &junc );
+                               VectorCopy( pt, junc.xyz );
+                               
+                               /* compare against existing verts */
+                               if( VectorCompare( junc.xyz, a->xyz ) || VectorCompare( junc.xyz, b->xyz ) || VectorCompare( junc.xyz, c->xyz ) )
+                                       continue;
+                               
+                               /* see if we can just re-use the existing vert */
+                               if( !memcmp( &metaVerts[ j ], &junc, sizeof( junc ) ) )
+                                       vertIndex = j;
+                               else
+                               {
+                                       /* find new vertex (note: a and b are invalid pointers after this) */
+                                       firstSearchMetaVert = numMetaVerts;
+                                       vertIndex = FindMetaVertex( &junc );
+                                       if( vertIndex < 0 )
+                                               continue;
+                               }
+                                               
+                               /* make new triangle */
+                               triIndex = AddMetaTriangle();
+                               if( triIndex < 0 )
+                                       continue;
+                               
+                               /* get triangles */
+                               tri = &metaTriangles[ i ];
+                               newTri = &metaTriangles[ triIndex ];
+                               
+                               /* copy the triangle */
+                               memcpy( newTri, tri, sizeof( *tri ) );
+                               
+                               /* fix verts */
+                               tri->indexes[ (k + 1) % 3 ] = vertIndex;
+                               newTri->indexes[ k ] = vertIndex;
+                               
+                               /* recalculate edges */
+                               CreateEdge( plane, metaVerts[ tri->indexes[ 0 ] ].xyz, metaVerts[ tri->indexes[ 1 ] ].xyz, &edges[ 0 ] );
+                               CreateEdge( plane, metaVerts[ tri->indexes[ 1 ] ].xyz, metaVerts[ tri->indexes[ 2 ] ].xyz, &edges[ 1 ] );
+                               CreateEdge( plane, metaVerts[ tri->indexes[ 2 ] ].xyz, metaVerts[ tri->indexes[ 0 ] ].xyz, &edges[ 2 ] );
+                               
+                               /* debug code */
+                               metaVerts[ vertIndex ].color[ 0 ][ 0 ] = 255;
+                               metaVerts[ vertIndex ].color[ 0 ][ 1 ] = 204;
+                               metaVerts[ vertIndex ].color[ 0 ][ 2 ] = 0;
+                               
+                               /* add to counter and end processing of this vert */
+                               numTJuncs++;
+                               break;
+                       }
+               }
+       }
+       
+       /* print time */
+       Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
+       
+       /* emit some stats */
+       Sys_FPrintf( SYS_VRB, "%9d T-junctions added\n", numTJuncs );
+}
+
+
+
+/*
+SmoothMetaTriangles()
+averages coincident vertex normals in the meta triangles
+*/
+
+#define MAX_SAMPLES                            256
+#define THETA_EPSILON                  0.000001
+#define EQUAL_NORMAL_EPSILON   0.01
+
+void SmoothMetaTriangles( void )
+{
+       int                             i, j, k, f, fOld, start, cs, numVerts, numVotes, numSmoothed;
+       float                   shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
+       metaTriangle_t  *tri;
+       float                   *shadeAngles;
+       byte                    *smoothed;
+       vec3_t                  average, diff;
+       int                             indexes[ MAX_SAMPLES ];
+       vec3_t                  votes[ MAX_SAMPLES ];
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- SmoothMetaTriangles ---\n" );
+       
+       /* allocate shade angle table */
+       shadeAngles = safe_malloc( numMetaVerts * sizeof( float ) );
+       memset( shadeAngles, 0, numMetaVerts * sizeof( float ) );
+       
+       /* allocate smoothed table */
+       cs = (numMetaVerts / 8) + 1;
+       smoothed = safe_malloc( cs );
+       memset( smoothed, 0, cs );
+       
+       /* set default shade angle */
+       defaultShadeAngle = DEG2RAD( npDegrees );
+       maxShadeAngle = 0.0f;
+       
+       /* run through every surface and flag verts belonging to non-lightmapped surfaces
+          and set per-vertex smoothing angle */
+       for( i = 0, tri = &metaTriangles[ i ]; i < numMetaTriangles; i++, tri++ )
+       {
+               /* get shader for shade angle */
+               if( tri->si->shadeAngleDegrees > 0.0f )
+                       shadeAngle = DEG2RAD( tri->si->shadeAngleDegrees );
+               else
+                       shadeAngle = defaultShadeAngle;
+               if( shadeAngle > maxShadeAngle )
+                       maxShadeAngle = shadeAngle;
+               
+               /* flag its verts */
+               for( j = 0; j < 3; j++ )
+               {
+                       shadeAngles[ tri->indexes[ j ] ] = shadeAngle;
+                       if( shadeAngle <= 0 )
+                               smoothed[ tri->indexes[ j ] >> 3 ] |= (1 << (tri->indexes[ j ] & 7));
+               }
+       }
+       
+       /* bail if no surfaces have a shade angle */
+       if( maxShadeAngle <= 0 )
+       {
+               Sys_FPrintf( SYS_VRB, "No smoothing angles specified, aborting\n" );
+               free( shadeAngles );
+               free( smoothed );
+               return;
+       }
+       
+       /* init pacifier */
+       fOld = -1;
+       start = I_FloatTime();
+       
+       /* go through the list of vertexes */
+       numSmoothed = 0;
+       for( i = 0; i < numMetaVerts; i++ )
+       {
+               /* print pacifier */
+               f = 10 * i / numMetaVerts;
+               if( f != fOld )
+               {
+                       fOld = f;
+                       Sys_FPrintf( SYS_VRB, "%d...", f );
+               }
+               
+               /* already smoothed? */
+               if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
+                       continue;
+               
+               /* clear */
+               VectorClear( average );
+               numVerts = 0;
+               numVotes = 0;
+               
+               /* build a table of coincident vertexes */
+               for( j = i; j < numMetaVerts && numVerts < MAX_SAMPLES; j++ )
+               {
+                       /* already smoothed? */
+                       if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
+                               continue;
+                       
+                       /* test vertexes */
+                       if( VectorCompare( metaVerts[ i ].xyz, metaVerts[ j ].xyz ) == qfalse )
+                               continue;
+                       
+                       /* use smallest shade angle */
+                       shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
+                       
+                       /* check shade angle */
+                       dot = DotProduct( metaVerts[ i ].normal, metaVerts[ j ].normal );
+                       if( dot > 1.0 )
+                               dot = 1.0;
+                       else if( dot < -1.0 )
+                               dot = -1.0;
+                       testAngle = acos( dot ) + THETA_EPSILON;
+                       if( testAngle >= shadeAngle )
+                               continue;
+                       
+                       /* add to the list */
+                       indexes[ numVerts++ ] = j;
+                       
+                       /* flag vertex */
+                       smoothed[ j >> 3 ] |= (1 << (j & 7));
+                       
+                       /* see if this normal has already been voted */
+                       for( k = 0; k < numVotes; k++ )
+                       {
+                               VectorSubtract( metaVerts[ j ].normal, votes[ k ], diff );
+                               if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
+                                       fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
+                                       fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
+                                       break;
+                       }
+                       
+                       /* add a new vote? */
+                       if( k == numVotes && numVotes < MAX_SAMPLES )
+                       {
+                               VectorAdd( average, metaVerts[ j ].normal, average );
+                               VectorCopy( metaVerts[ j ].normal, votes[ numVotes ] );
+                               numVotes++;
+                       }
+               }
+               
+               /* don't average for less than 2 verts */
+               if( numVerts < 2 )
+                       continue;
+               
+               /* average normal */
+               if( VectorNormalize( average, average ) > 0 )
+               {
+                       /* smooth */
+                       for( j = 0; j < numVerts; j++ )
+                               VectorCopy( average, metaVerts[ indexes[ j ] ].normal );
+                       numSmoothed++;
+               }
+       }
+       
+       /* free the tables */
+       free( shadeAngles );
+       free( smoothed );
+       
+       /* print time */
+       Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
+
+       /* emit some stats */
+       Sys_FPrintf( SYS_VRB, "%9d smoothed vertexes\n", numSmoothed );
+}
+
+
+
+/*
+AddMetaVertToSurface()
+adds a drawvert to a surface unless an existing vert matching already exists
+returns the index of that vert (or < 0 on failure)
+*/
+
+int AddMetaVertToSurface( mapDrawSurface_t *ds, bspDrawVert_t *dv1, int *coincident )
+{
+       int                             i;
+       bspDrawVert_t   *dv2;
+       
+       
+       /* go through the verts and find a suitable candidate */
+       for( i = 0; i < ds->numVerts; i++ )
+       {
+               /* get test vert */
+               dv2 = &ds->verts[ i ];
+               
+               /* compare xyz and normal */
+               if( VectorCompare( dv1->xyz, dv2->xyz ) == qfalse )
+                       continue;
+               if( VectorCompare( dv1->normal, dv2->normal ) == qfalse )
+                       continue;
+               
+               /* good enough at this point */
+               (*coincident)++;
+               
+               /* compare texture coordinates and color */
+               if( dv1->st[ 0 ] != dv2->st[ 0 ] || dv1->st[ 1 ] != dv2->st[ 1 ] )
+                       continue;
+               if( dv1->color[ 0 ][ 3 ] != dv2->color[ 0 ][ 3 ] )
+                       continue;
+               
+               /* found a winner */
+               numMergedVerts++;
+               return i;
+       }
+
+       /* overflow check */
+       if( ds->numVerts >= maxSurfaceVerts )
+               return VERTS_EXCEEDED;
+       
+       /* made it this far, add the vert and return */
+       dv2 = &ds->verts[ ds->numVerts++ ];
+       *dv2 = *dv1;
+       return (ds->numVerts - 1);
+}
+
+
+
+
+/*
+AddMetaTriangleToSurface()
+attempts to add a metatriangle to a surface
+returns the score of the triangle added
+*/
+
+#define AXIS_SCORE                     100000
+#define AXIS_MIN                       100000
+#define VERT_SCORE                     10000
+#define SURFACE_SCORE          1000
+#define ST_SCORE                       50
+#define ST_SCORE2                      (2 * (ST_SCORE))
+
+#define ADEQUATE_SCORE         ((AXIS_MIN) + 1 * (VERT_SCORE))
+#define GOOD_SCORE                     ((AXIS_MIN) + 2 * (VERT_SCORE) + 4 * (ST_SCORE))
+#define PERFECT_SCORE          ((AXIS_MIN) + + 3 * (VERT_SCORE) + (SURFACE_SCORE) + 4 * (ST_SCORE))
+
+static int AddMetaTriangleToSurface( mapDrawSurface_t *ds, metaTriangle_t *tri, qboolean testAdd )
+{
+       int                                     i, score, coincident, ai, bi, ci, oldTexRange[ 2 ];
+       float                           lmMax;
+       vec3_t                          mins, maxs;
+       qboolean                        inTexRange, es, et;
+       mapDrawSurface_t        old;
+       
+       
+       /* overflow check */
+       if( ds->numIndexes >= maxSurfaceIndexes )
+               return 0;
+       
+       /* test the triangle */
+       if( ds->entityNum != tri->entityNum )   /* ydnar: added 2002-07-06 */
+               return 0;
+       if( ds->castShadows != tri->castShadows || ds->recvShadows != tri->recvShadows )
+               return 0;
+       if( ds->shaderInfo != tri->si || ds->fogNum != tri->fogNum || ds->sampleSize != tri->sampleSize )
+               return 0;
+       #if 0
+               if( !(ds->shaderInfo->compileFlags & C_VERTEXLIT) &&
+                       //% VectorCompare( ds->lightmapAxis, tri->lightmapAxis ) == qfalse )
+                       DotProduct( ds->lightmapAxis, tri->plane ) < 0.25f )
+                       return 0;
+       #endif
+       
+       /* planar surfaces will only merge with triangles in the same plane */
+       if( npDegrees == 0.0f && ds->shaderInfo->nonplanar == qfalse && ds->planeNum >= 0 )
+       {
+               if( VectorCompare( mapplanes[ ds->planeNum ].normal, tri->plane ) == qfalse || mapplanes[ ds->planeNum ].dist != tri->plane[ 3 ] )
+                       return 0;
+               if( tri->planeNum >= 0 && tri->planeNum != ds->planeNum )
+                       return 0;
+       }
+       
+       /* set initial score */
+       score = tri->surfaceNum == ds->surfaceNum ? SURFACE_SCORE : 0;
+       
+       /* score the the dot product of lightmap axis to plane */
+       if( (ds->shaderInfo->compileFlags & C_VERTEXLIT) || VectorCompare( ds->lightmapAxis, tri->lightmapAxis ) )
+               score += AXIS_SCORE;
+       else
+               score += AXIS_SCORE * DotProduct( ds->lightmapAxis, tri->plane );
+       
+       /* preserve old drawsurface if this fails */
+       memcpy( &old, ds, sizeof( *ds ) );
+       
+       /* attempt to add the verts */
+       coincident = 0;
+       ai = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 0 ] ], &coincident );
+       bi = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 1 ] ], &coincident );
+       ci = AddMetaVertToSurface( ds, &metaVerts[ tri->indexes[ 2 ] ], &coincident );
+       
+       /* check vertex underflow */
+       if( ai < 0 || bi < 0 || ci < 0 )
+       {
+               memcpy( ds, &old, sizeof( *ds ) );
+               return 0;
+       }
+       
+       /* score coincident vertex count (2003-02-14: changed so this only matters on planar surfaces) */
+       score += (coincident * VERT_SCORE);
+       
+       /* add new vertex bounds to mins/maxs */
+       VectorCopy( ds->mins, mins );
+       VectorCopy( ds->maxs, maxs );
+       AddPointToBounds( metaVerts[ tri->indexes[ 0 ] ].xyz, mins, maxs );
+       AddPointToBounds( metaVerts[ tri->indexes[ 1 ] ].xyz, mins, maxs );
+       AddPointToBounds( metaVerts[ tri->indexes[ 2 ] ].xyz, mins, maxs );
+       
+       /* check lightmap bounds overflow (after at least 1 triangle has been added) */
+       if( !(ds->shaderInfo->compileFlags & C_VERTEXLIT) &&
+               ds->numIndexes > 0 && VectorLength( ds->lightmapAxis ) > 0.0f &&
+               (VectorCompare( ds->mins, mins ) == qfalse || VectorCompare( ds->maxs, maxs ) == qfalse) )
+       {
+               /* set maximum size before lightmap scaling (normally 2032 units) */
+               lmMax = (ds->sampleSize * (ds->shaderInfo->lmCustomWidth - 1));
+               for( i = 0; i < 3; i++ )
+               {
+                       if( (maxs[ i ] - mins[ i ]) > lmMax )
+                       {
+                               memcpy( ds, &old, sizeof( *ds ) );
+                               return 0;
+                       }
+               }
+       }
+       
+       /* check texture range overflow */
+       oldTexRange[ 0 ] = ds->texRange[ 0 ];
+       oldTexRange[ 1 ] = ds->texRange[ 1 ];
+       inTexRange = CalcSurfaceTextureRange( ds );
+       
+       es = (ds->texRange[ 0 ] > oldTexRange[ 0 ]) ? qtrue : qfalse;
+       et = (ds->texRange[ 1 ] > oldTexRange[ 1 ]) ? qtrue : qfalse;
+       
+       if( inTexRange == qfalse && ds->numIndexes > 0 )
+       {
+               memcpy( ds, &old, sizeof( *ds ) );
+               return UNSUITABLE_TRIANGLE;
+       }
+       
+       /* score texture range */
+       if( ds->texRange[ 0 ] <= oldTexRange[ 0 ] )
+               score += ST_SCORE2;
+       else if( ds->texRange[ 0 ] > oldTexRange[ 0 ] && oldTexRange[ 1 ] > oldTexRange[ 0 ] )
+               score += ST_SCORE;
+       
+       if( ds->texRange[ 1 ] <= oldTexRange[ 1 ] )
+               score += ST_SCORE2;
+       else if( ds->texRange[ 1 ] > oldTexRange[ 1 ] && oldTexRange[ 0 ] > oldTexRange[ 1 ] )
+               score += ST_SCORE;
+       
+       
+       /* go through the indexes and try to find an existing triangle that matches abc */
+       for( i = 0; i < ds->numIndexes; i += 3 )
+       {
+               /* 2002-03-11 (birthday!): rotate the triangle 3x to find an existing triangle */
+               if( (ai == ds->indexes[ i ] && bi == ds->indexes[ i + 1 ] && ci == ds->indexes[ i + 2 ]) ||
+                       (bi == ds->indexes[ i ] && ci == ds->indexes[ i + 1 ] && ai == ds->indexes[ i + 2 ]) ||
+                       (ci == ds->indexes[ i ] && ai == ds->indexes[ i + 1 ] && bi == ds->indexes[ i + 2 ]) )
+               {
+                       /* triangle already present */
+                       memcpy( ds, &old, sizeof( *ds ) );
+                       tri->si = NULL;
+                       return 0;
+               }
+               
+               /* rotate the triangle 3x to find an inverse triangle (error case) */
+               if( (ai == ds->indexes[ i ] && bi == ds->indexes[ i + 2 ] && ci == ds->indexes[ i + 1 ]) ||
+                       (bi == ds->indexes[ i ] && ci == ds->indexes[ i + 2 ] && ai == ds->indexes[ i + 1 ]) ||
+                       (ci == ds->indexes[ i ] && ai == ds->indexes[ i + 2 ] && bi == ds->indexes[ i + 1 ]) )
+               {
+                       /* warn about it */
+                       Sys_Printf( "WARNING: Flipped triangle: (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f)\n",
+                               ds->verts[ ai ].xyz[ 0 ], ds->verts[ ai ].xyz[ 1 ], ds->verts[ ai ].xyz[ 2 ],
+                               ds->verts[ bi ].xyz[ 0 ], ds->verts[ bi ].xyz[ 1 ], ds->verts[ bi ].xyz[ 2 ],
+                               ds->verts[ ci ].xyz[ 0 ], ds->verts[ ci ].xyz[ 1 ], ds->verts[ ci ].xyz[ 2 ] );
+                       
+                       /* reverse triangle already present */
+                       memcpy( ds, &old, sizeof( *ds ) );
+                       tri->si = NULL;
+                       return 0;
+               }
+       }
+       
+       /* add the triangle indexes */
+       if( ds->numIndexes < maxSurfaceIndexes )
+               ds->indexes[ ds->numIndexes++ ] = ai;
+       if( ds->numIndexes < maxSurfaceIndexes )
+               ds->indexes[ ds->numIndexes++ ] = bi;
+       if( ds->numIndexes < maxSurfaceIndexes )
+               ds->indexes[ ds->numIndexes++ ] = ci;
+       
+       /* check index overflow */
+       if( ds->numIndexes >= maxSurfaceIndexes  )
+       {
+               memcpy( ds, &old, sizeof( *ds ) );
+               return 0;
+       }
+       
+       /* sanity check the indexes */
+       if( ds->numIndexes >= 3 &&
+               (ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 2 ] ||
+               ds->indexes[ ds->numIndexes - 3 ] == ds->indexes[ ds->numIndexes - 1 ] ||
+               ds->indexes[ ds->numIndexes - 2 ] == ds->indexes[ ds->numIndexes - 1 ]) )
+               Sys_Printf( "DEG:%d! ", ds->numVerts );
+       
+       /* testing only? */
+       if( testAdd )
+               memcpy( ds, &old, sizeof( *ds ) );
+       else
+       {
+               /* copy bounds back to surface */
+               VectorCopy( mins, ds->mins );
+               VectorCopy( maxs, ds->maxs );
+               
+               /* mark triangle as used */
+               tri->si = NULL;
+       }
+       
+       /* add a side reference */
+       ds->sideRef = AllocSideRef( tri->side, ds->sideRef );
+       
+       /* return to sender */
+       return score;
+}
+
+
+
+/*
+MetaTrianglesToSurface()
+creates map drawsurface(s) from the list of possibles
+*/
+
+static void MetaTrianglesToSurface( int numPossibles, metaTriangle_t *possibles, int *fOld, int *numAdded )
+{
+       int                                     i, j, f, best, score, bestScore;
+       metaTriangle_t          *seed, *test;
+       mapDrawSurface_t        *ds;
+       bspDrawVert_t           *verts;
+       int                                     *indexes;
+       qboolean                        added;
+       
+       
+       /* allocate arrays */
+       verts = safe_malloc( sizeof( *verts ) * maxSurfaceVerts );
+       indexes = safe_malloc( sizeof( *indexes ) * maxSurfaceIndexes );
+       
+       /* walk the list of triangles */
+       for( i = 0, seed = possibles; i < numPossibles; i++, seed++ )
+       {
+               /* skip this triangle if it has already been merged */
+               if( seed->si == NULL )
+                       continue;
+               
+               /* -----------------------------------------------------------------
+                  initial drawsurf construction
+                  ----------------------------------------------------------------- */
+               
+               /* start a new drawsurface */
+               ds = AllocDrawSurface( SURFACE_META );
+               ds->entityNum = seed->entityNum;
+               ds->surfaceNum = seed->surfaceNum;
+               ds->castShadows = seed->castShadows;
+               ds->recvShadows = seed->recvShadows;
+               
+               ds->shaderInfo = seed->si;
+               ds->planeNum = seed->planeNum;
+               ds->fogNum = seed->fogNum;
+               ds->sampleSize = seed->sampleSize;
+               ds->verts = verts;
+               ds->indexes = indexes;
+               VectorCopy( seed->lightmapAxis, ds->lightmapAxis );
+               ds->sideRef = AllocSideRef( seed->side, NULL );
+               
+               ClearBounds( ds->mins, ds->maxs );
+               
+               /* clear verts/indexes */
+               memset( verts, 0, sizeof( verts ) );
+               memset( indexes, 0, sizeof( indexes ) );
+               
+               /* add the first triangle */
+               if( AddMetaTriangleToSurface( ds, seed, qfalse ) )
+                       (*numAdded)++;
+               
+               /* -----------------------------------------------------------------
+                  add triangles
+                  ----------------------------------------------------------------- */
+               
+               /* progressively walk the list until no more triangles can be added */
+               added = qtrue;
+               while( added )
+               {
+                       /* print pacifier */
+                       f = 10 * *numAdded / numMetaTriangles;
+                       if( f > *fOld )
+                       {
+                               *fOld = f;
+                               Sys_FPrintf( SYS_VRB, "%d...", f );
+                       }
+                       
+                       /* reset best score */
+                       best = -1;
+                       bestScore = 0;
+                       added = qfalse;
+                       
+                       /* walk the list of possible candidates for merging */
+                       for( j = i + 1, test = &possibles[ j ]; j < numPossibles; j++, test++ )
+                       {
+                               /* score this triangle */
+                               score = AddMetaTriangleToSurface( ds, test, qtrue );
+                               if( score > bestScore )
+                               {
+                                       best = j;
+                                       bestScore = score;
+                                       
+                                       /* if we have a score over a certain threshold, just use it */
+                                       if( bestScore >= GOOD_SCORE )
+                                       {
+                                               if( AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse ) )
+                                                       (*numAdded)++;
+                                               
+                                               /* reset */
+                                               best = -1;
+                                               bestScore = 0;
+                                               added = qtrue;
+                                       }
+                               }
+                       }
+                       
+                       /* add best candidate */
+                       if( best >= 0 && bestScore > ADEQUATE_SCORE )
+                       {
+                               if( AddMetaTriangleToSurface( ds, &possibles[ best ], qfalse ) )
+                                       (*numAdded)++;
+                               
+                               /* reset */
+                               added = qtrue;
+                       }
+               }
+               
+               /* copy the verts and indexes to the new surface */
+               ds->verts = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );
+               memcpy( ds->verts, verts, ds->numVerts * sizeof( bspDrawVert_t ) );
+               ds->indexes = safe_malloc( ds->numIndexes * sizeof( int ) );
+               memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( int ) );
+               
+               /* classify the surface */
+               ClassifySurfaces( 1, ds );
+               
+               /* add to count */
+               numMergedSurfaces++;
+       }
+       
+       /* free arrays */
+       free( verts );
+       free( indexes );
+}
+
+
+
+/*
+CompareMetaTriangles()
+compare function for qsort()
+*/
+
+static int CompareMetaTriangles( const void *a, const void *b )
+{
+       int             i, j, av, bv;
+       vec3_t  aMins, bMins;
+       
+       
+       /* shader first */
+       if( ((metaTriangle_t*) a)->si < ((metaTriangle_t*) b)->si )
+               return 1;
+       else if( ((metaTriangle_t*) a)->si > ((metaTriangle_t*) b)->si )
+               return -1;
+       
+       /* then fog */
+       else if( ((metaTriangle_t*) a)->fogNum < ((metaTriangle_t*) b)->fogNum )
+               return 1;
+       else if( ((metaTriangle_t*) a)->fogNum > ((metaTriangle_t*) b)->fogNum )
+               return -1;
+       
+       /* then position in world */
+       else
+       {
+               /* find mins */
+               VectorSet( aMins, 999999, 999999, 999999 );
+               VectorSet( bMins, 999999, 999999, 999999 );
+               for( i = 0; i < 3; i++ )
+               {
+                       av = ((metaTriangle_t*) a)->indexes[ i ];
+                       bv = ((metaTriangle_t*) b)->indexes[ i ];
+                       for( j = 0; j < 3; j++ )
+                       {
+                               if( metaVerts[ av ].xyz[ j ] < aMins[ j ] )
+                                       aMins[ j ] = metaVerts[ av ].xyz[ j ];
+                               if( metaVerts[ bv ].xyz[ j ] < bMins[ j ] )
+                                       bMins[ j ] = metaVerts[ bv ].xyz[ j ];
+                       }
+               }
+               
+               /* test it */
+               for( i = 0; i < 3; i++ )
+               {
+                       if( aMins[ i ] < bMins[ i ] )
+                               return 1;
+                       else if( aMins[ i ] > bMins[ i ] )
+                               return -1;
+               }
+       }
+       
+       /* functionally equivalent */
+       return 0;
+}
+
+
+
+/*
+MergeMetaTriangles()
+merges meta triangles into drawsurfaces
+*/
+
+void MergeMetaTriangles( void )
+{
+       int                                     i, j, fOld, start, numAdded;
+       metaTriangle_t          *head, *end;
+       
+       
+       /* only do this if there are meta triangles */
+       if( numMetaTriangles <= 0 )
+               return;
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- MergeMetaTriangles ---\n" );
+       
+       /* sort the triangles by shader major, fognum minor */
+       qsort( metaTriangles, numMetaTriangles, sizeof( metaTriangle_t ), CompareMetaTriangles );
+
+       /* init pacifier */
+       fOld = -1;
+       start = I_FloatTime();
+       numAdded = 0;
+       
+       /* merge */
+       for( i = 0; i < numMetaTriangles; i = j )
+       {
+               /* get head of list */
+               head = &metaTriangles[ i ];
+               
+               /* find end */
+               for( j = i + 1; j < numMetaTriangles; j++ )
+               {
+                       /* get end of list */
+                       end = &metaTriangles[ j ];
+                       if( head->si != end->si || head->fogNum != end->fogNum )
+                               break;
+               }
+               
+               /* try to merge this list of possible merge candidates */
+               MetaTrianglesToSurface( (j - i), head, &fOld, &numAdded );
+       }
+       
+       /* clear meta triangle list */
+       ClearMetaTriangles();
+       
+       /* print time */
+       if( i )
+               Sys_FPrintf( SYS_VRB, " (%d)\n", (int) (I_FloatTime() - start) );
+       
+       /* emit some stats */
+       Sys_FPrintf( SYS_VRB, "%9d surfaces merged\n", numMergedSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d vertexes merged\n", numMergedVerts );
+}