]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/surface_meta.c
Merge branch 'master' of github.com:TTimo/GtkRadiant
[xonotic/netradiant.git] / tools / quake3 / q3map2 / surface_meta.c
index 74307a762c369ec52d5c4033f82401c6fafeefca..77ed73604e7e95c72b481081c7c381e65f8ebf10 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 >= ( ( ds->shaderInfo->compileFlags & C_VERTEXLIT ) ? maxSurfaceVerts : maxLMSurfaceVerts ) ) {
+               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) */
+               /* 2004-02-24: scale lightmap test size by 2 to catch larger brush faces */
+               /* 2004-04-11: reverting to actual lightmap size */
+               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++ )
+                       {
+                               /* skip this triangle if it has already been merged */
+                               if ( test->si == NULL ) {
+                                       continue;
+                               }
+
+                               /* 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 plane */
+       #if 0
+       else if ( npDegrees == 0.0f && ( (metaTriangle_t*) a )->si->nonplanar == qfalse &&
+                         ( (metaTriangle_t*) a )->planeNum >= 0 && ( (metaTriangle_t*) a )->planeNum >= 0 ) {
+               if ( ( (metaTriangle_t*) a )->plane[ 3 ] < ( (metaTriangle_t*) b )->plane[ 3 ] ) {
+                       return 1;
+               }
+               else if ( ( (metaTriangle_t*) a )->plane[ 3 ] > ( (metaTriangle_t*) b )->plane[ 3 ] ) {
+                       return -1;
+               }
+               else if ( ( (metaTriangle_t*) a )->plane[ 0 ] < ( (metaTriangle_t*) b )->plane[ 0 ] ) {
+                       return 1;
+               }
+               else if ( ( (metaTriangle_t*) a )->plane[ 0 ] > ( (metaTriangle_t*) b )->plane[ 0 ] ) {
+                       return -1;
+               }
+               else if ( ( (metaTriangle_t*) a )->plane[ 1 ] < ( (metaTriangle_t*) b )->plane[ 1 ] ) {
+                       return 1;
+               }
+               else if ( ( (metaTriangle_t*) a )->plane[ 1 ] > ( (metaTriangle_t*) b )->plane[ 1 ] ) {
+                       return -1;
+               }
+               else if ( ( (metaTriangle_t*) a )->plane[ 2 ] < ( (metaTriangle_t*) b )->plane[ 2 ] ) {
+                       return 1;
+               }
+               else if ( ( (metaTriangle_t*) a )->plane[ 2 ] > ( (metaTriangle_t*) b )->plane[ 2 ] ) {
+                       return -1;
+               }
+       }
+       #endif
+
+       /* then position in world */
+
+       /* 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, j = 0; i < numMetaTriangles; i = j )
+       {
+               /* get head of list */
+               head = &metaTriangles[ i ];
+
+               /* skip this triangle if it has already been merged */
+               if ( head->si == NULL ) {
+                       continue;
+               }
+
+               /* find end */
+               if ( j <= i ) {
+                       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 );
+}