]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/surface.c
set eol-style
[xonotic/netradiant.git] / tools / quake3 / q3map2 / surface.c
index dfd5c6e58ec870df980dc1a1371c27f274481d59..79649c89d58d915155bea343a610bfd7df6ea5e4 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_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-/*\r
-\r
-this section handles drawsurface allocation and creation\r
-\r
-*/\r
-\r
-/*\r
-AllocDrawSurface()\r
-ydnar: gs mods: changed to force an explicit type when allocating\r
-*/\r
-\r
-mapDrawSurface_t *AllocDrawSurface( surfaceType_t type )\r
-{\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* ydnar: gs mods: only allocate valid types */\r
-       if( type <= SURFACE_BAD || type >= NUM_SURFACE_TYPES )\r
-               Error( "AllocDrawSurface: Invalid surface type %d specified", type );\r
-       \r
-       /* bounds check */\r
-       if( numMapDrawSurfs >= MAX_MAP_DRAW_SURFS )\r
-               Error( "MAX_MAP_DRAW_SURFS (%d) exceeded", MAX_MAP_DRAW_SURFS );\r
-       ds = &mapDrawSurfs[ numMapDrawSurfs ];\r
-       numMapDrawSurfs++;\r
-       \r
-       /* ydnar: do initial surface setup */\r
-       memset( ds, 0, sizeof( mapDrawSurface_t ) );\r
-       ds->type = type;\r
-       ds->planeNum = -1;\r
-       ds->fogNum = defaultFogNum;                             /* ydnar 2003-02-12 */\r
-       ds->outputNum = -1;                                             /* ydnar 2002-08-13 */\r
-       ds->surfaceNum = numMapDrawSurfs - 1;   /* ydnar 2003-02-16 */\r
-       \r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-FinishSurface()\r
-ydnar: general surface finish pass\r
-*/\r
-\r
-void FinishSurface( mapDrawSurface_t *ds )\r
-{\r
-       /* dummy check */\r
-       if( ds == NULL || ds->shaderInfo == NULL )\r
-               return;\r
-       \r
-       /* ydnar: rocking tek-fu celshading */\r
-       if( ds->celShader != NULL )\r
-               MakeCelSurface( ds, ds->celShader );\r
-       \r
-       /* ydnar: rocking surface cloning (fur baby yeah!) */\r
-       if( ds->shaderInfo->cloneShader[ 0 ] != '\0' )\r
-               CloneSurface( ds, ShaderInfoForShader( ds->shaderInfo->cloneShader ) );\r
-}\r
-\r
-\r
-\r
-/*\r
-CloneSurface()\r
-clones a map drawsurface, using the specified shader\r
-*/\r
-\r
-mapDrawSurface_t *CloneSurface( mapDrawSurface_t *src, shaderInfo_t *si )\r
-{\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( src == NULL || si == NULL )\r
-               return NULL;\r
-       \r
-       /* allocate a new surface */\r
-       ds = AllocDrawSurface( src->type );\r
-       if( ds == NULL )\r
-               return NULL;\r
-       \r
-       /* copy it */\r
-       memcpy( ds, src, sizeof( *ds ) );\r
-       \r
-       /* destroy side reference */\r
-       ds->sideRef = NULL;\r
-       \r
-       /* set shader */\r
-       ds->shaderInfo = si;\r
-       \r
-       /* copy verts */\r
-       if( ds->numVerts > 0 )\r
-       {\r
-               ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
-               memcpy( ds->verts, src->verts, ds->numVerts * sizeof( *ds->verts ) );\r
-       }\r
-       \r
-       /* copy indexes */\r
-       if( ds->numIndexes <= 0 )\r
-               return ds;\r
-       ds->indexes = safe_malloc( ds->numIndexes * sizeof( *ds->indexes ) );\r
-       memcpy( ds->indexes, src->indexes, ds->numIndexes * sizeof( *ds->indexes ) );\r
-       \r
-       /* return the surface */\r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-MakeCelSurface() - ydnar\r
-makes a copy of a surface, but specific to cel shading\r
-*/\r
-\r
-mapDrawSurface_t *MakeCelSurface( mapDrawSurface_t *src, shaderInfo_t *si )\r
-{\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( src == NULL || si == NULL )\r
-               return NULL;\r
-       \r
-       /* don't create cel surfaces for certain types of shaders */\r
-       if( (src->shaderInfo->compileFlags & C_TRANSLUCENT) ||\r
-               (src->shaderInfo->compileFlags & C_SKY) )\r
-               return NULL;\r
-       \r
-       /* make a copy */\r
-       ds = CloneSurface( src, si );\r
-       if( ds == NULL )\r
-               return NULL;\r
-       \r
-       /* do some fixups for celshading */\r
-       ds->planar = qfalse;\r
-       ds->planeNum = -1;\r
-       \r
-       /* return the surface */\r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-MakeSkyboxSurface() - ydnar\r
-generates a skybox surface, viewable from everywhere there is sky\r
-*/\r
-\r
-mapDrawSurface_t *MakeSkyboxSurface( mapDrawSurface_t *src )\r
-{\r
-       int                                     i;\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( src == NULL )\r
-               return NULL;\r
-       \r
-       /* make a copy */\r
-       ds = CloneSurface( src, src->shaderInfo );\r
-       if( ds == NULL )\r
-               return NULL;\r
-       \r
-       /* set parent */\r
-       ds->parent = src;\r
-       \r
-       /* scale the surface vertexes */\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-       {\r
-               m4x4_transform_point( skyboxTransform, ds->verts[ i ].xyz );\r
-               \r
-               /* debug code */\r
-               //%     bspDrawVerts[ bspDrawSurfaces[ ds->outputNum ].firstVert + i ].color[ 0 ][ 1 ] = 0;\r
-               //%     bspDrawVerts[ bspDrawSurfaces[ ds->outputNum ].firstVert + i ].color[ 0 ][ 2 ] = 0;\r
-       }\r
-       \r
-       /* so backface culling creep doesn't bork the surface */\r
-       VectorClear( ds->lightmapVecs[ 2 ] );\r
-       \r
-       /* return the surface */\r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-IsTriangleDegenerate\r
-returns qtrue if all three points are colinear, backwards, or the triangle is just plain bogus\r
-*/\r
-\r
-#define        TINY_AREA       1.0f\r
-\r
-qboolean IsTriangleDegenerate( bspDrawVert_t *points, int a, int b, int c )\r
-{\r
-       vec3_t          v1, v2, v3;\r
-       float           d;\r
-       \r
-       \r
-       /* calcuate the area of the triangle */\r
-       VectorSubtract( points[ b ].xyz, points[ a ].xyz, v1 );\r
-       VectorSubtract( points[ c ].xyz, points[ a ].xyz, v2 );\r
-       CrossProduct( v1, v2, v3 );\r
-       d = VectorLength( v3 );\r
-       \r
-       /* assume all very small or backwards triangles will cause problems */\r
-       if( d < TINY_AREA )\r
-               return qtrue;\r
-       \r
-       /* must be a good triangle */\r
-       return qfalse;\r
-}\r
-\r
-\r
-\r
-/*\r
-ClearSurface() - ydnar\r
-clears a surface and frees any allocated memory\r
-*/\r
-\r
-void ClearSurface( mapDrawSurface_t *ds )\r
-{\r
-       ds->type = SURFACE_BAD;\r
-       ds->planar = qfalse;\r
-       ds->planeNum = -1;\r
-       ds->numVerts = 0;\r
-       if( ds->verts != NULL )\r
-               free( ds->verts );\r
-       ds->verts = NULL;\r
-       ds->numIndexes = 0;\r
-       if( ds->indexes != NULL )\r
-               free( ds->indexes );\r
-       ds->indexes = NULL;\r
-       numClearedSurfaces++;\r
-}\r
-\r
-\r
-\r
-/*\r
-TidyEntitySurfaces() - ydnar\r
-deletes all empty or bad surfaces from the surface list\r
-*/\r
-\r
-void TidyEntitySurfaces( entity_t *e )\r
-{\r
-       int                                     i, j, deleted;\r
-       mapDrawSurface_t        *out, *in;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- TidyEntitySurfaces ---\n" );\r
-       \r
-       /* walk the surface list */\r
-       deleted = 0;\r
-       for( i = e->firstDrawSurf, j = e->firstDrawSurf; j < numMapDrawSurfs; i++, j++ )\r
-       {\r
-               /* get out surface */\r
-               out = &mapDrawSurfs[ i ];\r
-               \r
-               /* walk the surface list again until a proper surface is found */\r
-               for( j; j < numMapDrawSurfs; j++ )\r
-               {\r
-                       /* get in surface */\r
-                       in = &mapDrawSurfs[ j ];\r
-                       \r
-                       /* this surface ok? */\r
-                       if( in->type == SURFACE_FLARE || in->type == SURFACE_SHADER ||\r
-                               (in->type != SURFACE_BAD && in->numVerts > 0) )\r
-                               break;\r
-                       \r
-                       /* nuke it */\r
-                       ClearSurface( in );\r
-                       deleted++;\r
-               }\r
-               \r
-               /* copy if necessary */\r
-               if( i != j )\r
-                       memcpy( out, in, sizeof( mapDrawSurface_t ) );\r
-       }\r
-       \r
-       /* set the new number of drawsurfs */\r
-       numMapDrawSurfs = i;\r
-       \r
-       /* emit some stats */\r
-       Sys_FPrintf( SYS_VRB, "%9d empty or malformed surfaces deleted\n", deleted );\r
-}\r
-\r
-\r
-\r
-/*\r
-CalcSurfaceTextureRange() - ydnar\r
-calculates the clamped texture range for a given surface, returns qtrue if it's within [-texRange,texRange]\r
-*/\r
-\r
-qboolean CalcSurfaceTextureRange( mapDrawSurface_t *ds )\r
-{\r
-       int             i, j, v, size[ 2 ];\r
-       float   mins[ 2 ], maxs[ 2 ];\r
-       \r
-       \r
-       /* try to early out */\r
-       if( ds->numVerts <= 0 )\r
-               return qtrue;\r
-       \r
-       /* walk the verts and determine min/max st values */\r
-       mins[ 0 ] = 999999;\r
-       mins[ 1 ] = 999999;\r
-       maxs[ 0 ] = -999999;\r
-       maxs[ 1 ] = -999999;\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-       {\r
-               for( j = 0; j < 2; j++ )\r
-               {\r
-                       if( ds->verts[ i ].st[ j ] < mins[ j ] )\r
-                               mins[ j ] = ds->verts[ i ].st[ j ];\r
-                       if( ds->verts[ i ].st[ j ] > maxs[ j ] )\r
-                               maxs[ j ] = ds->verts[ i ].st[ j ];\r
-               }\r
-       }\r
-       \r
-       /* clamp to integer range and calculate surface bias values */\r
-       for( j = 0; j < 2; j++ )\r
-               ds->bias[ j ] = -floor( 0.5f * (mins[ j ] + maxs[ j ]) );\r
-       \r
-       /* find biased texture coordinate mins/maxs */\r
-       size[ 0 ] = ds->shaderInfo->shaderWidth;\r
-       size[ 1 ] = ds->shaderInfo->shaderHeight;\r
-       ds->texMins[ 0 ] = 999999;\r
-       ds->texMins[ 1 ] = 999999;\r
-       ds->texMaxs[ 0 ] = -999999;\r
-       ds->texMaxs[ 1 ] = -999999;\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-       {\r
-               for( j = 0; j < 2; j++ )\r
-               {\r
-                       v = ((float) ds->verts[ i ].st[ j ] + ds->bias[ j ]) * size[ j ];\r
-                       if( v < ds->texMins[ j ] )\r
-                               ds->texMins[ j ] = v;\r
-                       if( v > ds->texMaxs[ j ] )\r
-                               ds->texMaxs[ j ] = v;\r
-               }\r
-       }\r
-       \r
-       /* calc ranges */\r
-       for( j = 0; j < 2; j++ )\r
-               ds->texRange[ j ] = (ds->texMaxs[ j ] - ds->texMins[ j ]);\r
-       \r
-       /* if range is zero, then assume unlimited precision */\r
-       if( texRange == 0 )\r
-               return qtrue;\r
-       \r
-       /* within range? */\r
-       for( j = 0; j < 2; j++ )\r
-       {\r
-               if( ds->texMins[ j ] < -texRange || ds->texMaxs[ j ] > texRange )\r
-                       return qfalse;\r
-       }\r
-       \r
-       /* within range */\r
-       return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-CalcLightmapAxis() - ydnar\r
-gives closed lightmap axis for a plane normal\r
-*/\r
-\r
-qboolean CalcLightmapAxis( vec3_t normal, vec3_t axis )\r
-{\r
-       vec3_t  absolute;\r
-               \r
-       \r
-       /* test */\r
-       if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && normal[ 2 ] == 0.0f )\r
-       {\r
-               VectorClear( axis );\r
-               return qfalse;\r
-       }\r
-       \r
-       /* get absolute normal */\r
-       absolute[ 0 ] = fabs( normal[ 0 ] );\r
-       absolute[ 1 ] = fabs( normal[ 1 ] );\r
-       absolute[ 2 ] = fabs( normal[ 2 ] );\r
-       \r
-       /* test and set */\r
-       if( absolute[ 2 ] > absolute[ 0 ] - 0.0001f && absolute[ 2 ] > absolute[ 1 ] - 0.0001f )\r
-       {\r
-               if( normal[ 2 ] > 0.0f )\r
-                       VectorSet( axis, 0.0f, 0.0f, 1.0f );\r
-               else\r
-                       VectorSet( axis, 0.0f, 0.0f, -1.0f );\r
-       }\r
-       else if( absolute[ 0 ] > absolute[ 1 ] - 0.0001f && absolute[ 0 ] > absolute[ 2 ] - 0.0001f )\r
-       {\r
-               if( normal[ 0 ] > 0.0f )\r
-                       VectorSet( axis, 1.0f, 0.0f, 0.0f );\r
-               else\r
-                       VectorSet( axis, -1.0f, 0.0f, 0.0f );\r
-       }\r
-       else\r
-       {\r
-               if( normal[ 1 ] > 0.0f )\r
-                       VectorSet( axis, 0.0f, 1.0f, 0.0f );\r
-               else\r
-                       VectorSet( axis, 0.0f, -1.0f, 0.0f );\r
-       }\r
-       \r
-       /* return ok */\r
-       return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-ClassifySurfaces() - ydnar\r
-fills out a bunch of info in the surfaces, including planar status, lightmap projection, and bounding box\r
-*/\r
-\r
-#define PLANAR_EPSILON 0.5f    //% 0.126f 0.25f\r
-\r
-void ClassifySurfaces( int numSurfs, mapDrawSurface_t *ds )\r
-{\r
-       int                                     i, bestAxis;\r
-       float                           dist;\r
-       vec4_t                          plane;\r
-       shaderInfo_t            *si;\r
-       static vec3_t           axii[ 6 ] =\r
-                                               {\r
-                                                       { 0, 0, -1 },\r
-                                                       { 0, 0, 1 },\r
-                                                       { -1, 0, 0 },\r
-                                                       { 1, 0, 0 },\r
-                                                       { 0, -1, 0 },\r
-                                                       { 0, 1, 0 }\r
-                                               };\r
-       \r
-       \r
-       /* walk the list of surfaces */\r
-       for( numSurfs; numSurfs > 0; numSurfs--, ds++ )\r
-       {\r
-               /* ignore bogus (or flare) surfaces */\r
-               if( ds->type == SURFACE_BAD || ds->numVerts <= 0 )\r
-                       continue;\r
-               \r
-               /* get shader */\r
-               si = ds->shaderInfo;\r
-               \r
-               /* -----------------------------------------------------------------\r
-                  force meta if vertex count is too high or shader requires it\r
-                  ----------------------------------------------------------------- */\r
-               \r
-               if( ds->type != SURFACE_PATCH && ds->type != SURFACE_FACE )\r
-               {\r
-                       if( ds->numVerts > SHADER_MAX_VERTEXES )\r
-                               ds->type = SURFACE_FORCED_META;\r
-               }\r
-               \r
-               /* -----------------------------------------------------------------\r
-                  plane and bounding box classification \r
-                  ----------------------------------------------------------------- */\r
-               \r
-               /* set surface bounding box */\r
-               ClearBounds( ds->mins, ds->maxs );\r
-               for( i = 0; i < ds->numVerts; i++ )\r
-                       AddPointToBounds( ds->verts[ i ].xyz, ds->mins, ds->maxs );\r
-               \r
-               /* try to get an existing plane */\r
-               if( ds->planeNum >= 0 )\r
-               {\r
-                       VectorCopy( mapplanes[ ds->planeNum ].normal, plane );\r
-                       plane[ 3 ] = mapplanes[ ds->planeNum ].dist;\r
-               }\r
-               \r
-               /* construct one from the first vert with a valid normal */\r
-               else\r
-               {\r
-                       VectorClear( plane );\r
-                       plane[ 3 ] = 0.0f;\r
-                       for( i = 0; i < ds->numVerts; i++ )\r
-                       {\r
-                               if( ds->verts[ i ].normal[ 0 ] != 0.0f && ds->verts[ i ].normal[ 1 ] != 0.0f && ds->verts[ i ].normal[ 2 ] != 0.0f )\r
-                               {\r
-                                       VectorCopy( ds->verts[ i ].normal, plane );\r
-                                       plane[ 3 ] = DotProduct( ds->verts[ i ].xyz, plane );\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               /* test for bogus plane */\r
-               if( VectorLength( plane ) <= 0.0f )\r
-               {\r
-                       ds->planar = qfalse;\r
-                       ds->planeNum = -1;\r
-               }\r
-               else\r
-               {\r
-                       /* determine if surface is planar */\r
-                       ds->planar = qtrue;\r
-                       \r
-                       /* test each vert */\r
-                       for( i = 0; i < ds->numVerts; i++ )\r
-                       {\r
-                               /* point-plane test */\r
-                               dist = DotProduct( ds->verts[ i ].xyz, plane ) - plane[ 3 ];\r
-                               if( fabs( dist ) > PLANAR_EPSILON )\r
-                               {\r
-                                       //%     if( ds->planeNum >= 0 )\r
-                                       //%     {\r
-                                       //%             Sys_Printf( "WARNING: Planar surface marked unplanar (%f > %f)\n", fabs( dist ), PLANAR_EPSILON );\r
-                                       //%             ds->verts[ i ].color[ 0 ][ 0 ] = ds->verts[ i ].color[ 0 ][ 2 ] = 0;\r
-                                       //%     }\r
-                                       ds->planar = qfalse;\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               /* find map plane if necessary */\r
-               if( ds->planar )\r
-               {\r
-                       if( ds->planeNum < 0 )\r
-                               ds->planeNum = FindFloatPlane( plane, plane[ 3 ], 1, &ds->verts[ 0 ].xyz );\r
-                       VectorCopy( plane, ds->lightmapVecs[ 2 ] );\r
-               }\r
-               else\r
-               {\r
-                       ds->planeNum = -1;\r
-                       VectorClear( ds->lightmapVecs[ 2 ] );\r
-                       //% if( ds->type == SURF_META || ds->type == SURF_FACE )\r
-                       //%             Sys_Printf( "WARNING: Non-planar face (%d): %s\n", ds->planeNum, ds->shaderInfo->shader );\r
-               }\r
-               \r
-               /* -----------------------------------------------------------------\r
-                  lightmap bounds and axis projection\r
-                  ----------------------------------------------------------------- */\r
-               \r
-               /* vertex lit surfaces don't need this information */\r
-               if( si->compileFlags & C_VERTEXLIT || ds->type == SURFACE_TRIANGLES )\r
-               {\r
-                       VectorClear( ds->lightmapAxis );\r
-                       //%     VectorClear( ds->lightmapVecs[ 2 ] );\r
-                       ds->sampleSize = 0;\r
-                       continue;\r
-               }\r
-               \r
-               /* the shader can specify an explicit lightmap axis */\r
-               if( si->lightmapAxis[ 0 ] || si->lightmapAxis[ 1 ] || si->lightmapAxis[ 2 ] )\r
-                       VectorCopy( si->lightmapAxis, ds->lightmapAxis );\r
-               else if( ds->type == SURFACE_FORCED_META )\r
-                       VectorClear( ds->lightmapAxis );\r
-               else if( ds->planar )\r
-                       CalcLightmapAxis( plane, ds->lightmapAxis );\r
-               else\r
-               {\r
-                       /* find best lightmap axis */\r
-                       for( bestAxis = 0; bestAxis < 6; bestAxis++ )\r
-                       {\r
-                               for( i = 0; i < ds->numVerts && bestAxis < 6; i++ )\r
-                               {\r
-                                       //% Sys_Printf( "Comparing %1.3f %1.3f %1.3f to %1.3f %1.3f %1.3f\n",\r
-                                       //%     ds->verts[ i ].normal[ 0 ], ds->verts[ i ].normal[ 1 ], ds->verts[ i ].normal[ 2 ],\r
-                                       //%     axii[ bestAxis ][ 0 ], axii[ bestAxis ][ 1 ], axii[ bestAxis ][ 2 ] );\r
-                                       if( DotProduct( ds->verts[ i ].normal, axii[ bestAxis ] ) < 0.25f )     /* fixme: adjust this tolerance to taste */\r
-                                               break;\r
-                               }\r
-                               \r
-                               if( i == ds->numVerts )\r
-                                       break;\r
-                       }\r
-                       \r
-                       /* set axis if possible */\r
-                       if( bestAxis < 6 )\r
-                       {\r
-                               //% if( ds->type == SURFACE_PATCH )\r
-                               //%     Sys_Printf( "Mapped axis %d onto patch\n", bestAxis );\r
-                               VectorCopy( axii[ bestAxis ], ds->lightmapAxis );\r
-                       }\r
-                       \r
-                       /* debug code */\r
-                       //% if( ds->type == SURFACE_PATCH )\r
-                       //%     Sys_Printf( "Failed to map axis %d onto patch\n", bestAxis );\r
-               }\r
-               \r
-               /* get lightmap sample size */\r
-               if( ds->sampleSize <= 0 )\r
-               {\r
-                       ds->sampleSize = sampleSize;\r
-                       if( ds->shaderInfo->lightmapSampleSize )\r
-                               ds->sampleSize = ds->shaderInfo->lightmapSampleSize;\r
-                       if( ds->lightmapScale > 0 )\r
-                               ds->sampleSize *= ds->lightmapScale;\r
-                       if( ds->sampleSize <= 0 )\r
-                               ds->sampleSize = 1;\r
-                       else if( ds->sampleSize > 16384 )       /* powers of 2 are preferred */\r
-                               ds->sampleSize = 16384;\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-ClassifyEntitySurfaces() - ydnar\r
-classifies all surfaces in an entity\r
-*/\r
-\r
-void ClassifyEntitySurfaces( entity_t *e )\r
-{\r
-       int             i;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- ClassifyEntitySurfaces ---\n" );\r
-       \r
-       /* walk the surface list */\r
-       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )\r
-               ClassifySurfaces( 1, &mapDrawSurfs[ i ] );\r
-       \r
-       /* tidy things up */\r
-       TidyEntitySurfaces( e );\r
-}\r
-\r
-\r
-\r
-/*\r
-GetShaderIndexForPoint() - ydnar\r
-for shader-indexed surfaces (terrain), find a matching index from the indexmap\r
-*/\r
-\r
-byte GetShaderIndexForPoint( indexMap_t *im, vec3_t eMins, vec3_t eMaxs, vec3_t point )\r
-{\r
-       int                     i, x, y;\r
-       float           s, t;\r
-       vec3_t          mins, maxs, size;\r
-       \r
-       \r
-       /* early out if no indexmap */\r
-       if( im == NULL )\r
-               return 0;\r
-       \r
-       /* this code is really broken */\r
-       #if 0\r
-               /* legacy precision fudges for terrain */\r
-               for( i = 0; i < 3; i++ )\r
-               {\r
-                       mins[ i ] = floor( eMins[ i ] + 0.1 );\r
-                       maxs[ i ] = floor( eMaxs[ i ] + 0.1 );\r
-                       size[ i ] = maxs[ i ] - mins[ i ];\r
-               }\r
-               \r
-               /* find st (fixme: support more than just z-axis projection) */\r
-               s = floor( point[ 0 ] + 0.1f - mins[ 0 ] ) / size[ 0 ];\r
-               t = floor( maxs[ 1 ] - point[ 1 ] + 0.1f ) / size[ 1 ];\r
-               if( s < 0.0f )\r
-                       s = 0.0f;\r
-               else if( s > 1.0f )\r
-                       s = 1.0f;\r
-               if( t < 0.0f )\r
-                       t = 0.0f;\r
-               else if( t > 1.0f )\r
-                       t = 1.0f;\r
-               \r
-               /* make xy */\r
-               x = (im->w - 1) * s;\r
-               y = (im->h - 1) * t;\r
-       #else\r
-               /* get size */\r
-               for( i = 0; i < 3; i++ )\r
-               {\r
-                       mins[ i ] = eMins[ i ];\r
-                       maxs[ i ] = eMaxs[ i ];\r
-                       size[ i ] = maxs[ i ] - mins[ i ];\r
-               }\r
-               \r
-               /* calc st */\r
-               s = (point[ 0 ] - mins[ 0 ]) / size[ 0 ];\r
-               t = (maxs[ 1 ] - point[ 1 ]) / size[ 1 ];\r
-               \r
-               /* calc xy */\r
-               x = s * im->w;\r
-               y = t * im->h;\r
-               if( x < 0 )\r
-                       x = 0;\r
-               else if( x > (im->w - 1) )\r
-                       x = (im->w - 1);\r
-               if( y < 0 )\r
-                       y = 0;\r
-               else if( y > (im->h - 1) )\r
-                       y = (im->h - 1);\r
-       #endif\r
-       \r
-       /* return index */\r
-       return im->pixels[ y * im->w + x ];\r
-}\r
-\r
-\r
-\r
-/*\r
-GetIndexedShader() - ydnar\r
-for a given set of indexes and an indexmap, get a shader and set the vertex alpha in-place\r
-this combines a couple different functions from terrain.c\r
-*/\r
-\r
-shaderInfo_t *GetIndexedShader( shaderInfo_t *parent, indexMap_t *im, int numPoints, byte *shaderIndexes )\r
-{\r
-       int                             i;\r
-       byte                    minShaderIndex, maxShaderIndex;\r
-       char                    shader[ MAX_QPATH ];\r
-       shaderInfo_t    *si;\r
-       \r
-       \r
-       /* early out if bad data */\r
-       if( im == NULL || numPoints <= 0 || shaderIndexes == NULL )\r
-               return ShaderInfoForShader( "default" );\r
-       \r
-       /* determine min/max index */\r
-       minShaderIndex = 255;\r
-       maxShaderIndex = 0;\r
-       for( i = 0; i < numPoints; i++ )\r
-       {\r
-               if( shaderIndexes[ i ] < minShaderIndex )\r
-                       minShaderIndex = shaderIndexes[ i ];\r
-               if( shaderIndexes[ i ] > maxShaderIndex )\r
-                       maxShaderIndex = shaderIndexes[ i ];\r
-       }\r
-       \r
-       /* set alpha inline */\r
-       for( i = 0; i < numPoints; i++ )\r
-       {\r
-               /* straight rip from terrain.c */\r
-               if( shaderIndexes[ i ] < maxShaderIndex )\r
-                       shaderIndexes[ i ] = 0;\r
-               else\r
-                       shaderIndexes[ i ] = 255;\r
-       }\r
-       \r
-       /* make a shader name */\r
-       if( minShaderIndex == maxShaderIndex )\r
-               sprintf( shader, "textures/%s_%d", im->shader, maxShaderIndex );\r
-       else\r
-               sprintf( shader, "textures/%s_%dto%d", im->shader, minShaderIndex, maxShaderIndex );\r
-       \r
-       /* get the shader */\r
-       si = ShaderInfoForShader( shader );\r
-       \r
-       /* inherit a few things from parent shader */\r
-       if( parent->globalTexture )\r
-               si->globalTexture = qtrue;\r
-       if( parent->forceMeta )\r
-               si->forceMeta = qtrue;\r
-       if( parent->nonplanar )\r
-               si->nonplanar = qtrue;\r
-       if( si->shadeAngleDegrees == 0.0 )\r
-               si->shadeAngleDegrees = parent->shadeAngleDegrees;\r
-       if( parent->tcGen && si->tcGen == qfalse )\r
-       {\r
-               /* set xy texture projection */\r
-               si->tcGen = qtrue;\r
-               VectorCopy( parent->vecs[ 0 ], si->vecs[ 0 ] );\r
-               VectorCopy( parent->vecs[ 1 ], si->vecs[ 1 ] );\r
-       }\r
-       if( VectorLength( parent->lightmapAxis ) > 0.0f && VectorLength( si->lightmapAxis ) <= 0.0f )\r
-       {\r
-               /* set lightmap projection axis */\r
-               VectorCopy( parent->lightmapAxis, si->lightmapAxis );\r
-       }\r
-       \r
-       /* return the shader */\r
-       return si;\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-DrawSurfaceForSide()\r
-creates a SURF_FACE drawsurface from a given brush side and winding\r
-*/\r
-\r
-#define        SNAP_FLOAT_TO_INT       8\r
-#define        SNAP_INT_TO_FLOAT       (1.0 / SNAP_FLOAT_TO_INT)\r
-\r
-mapDrawSurface_t *DrawSurfaceForSide( entity_t *e, brush_t *b, side_t *s, winding_t *w )\r
-{\r
-       int                                     i, j, k;\r
-       mapDrawSurface_t        *ds;\r
-       shaderInfo_t            *si, *parent;\r
-       bspDrawVert_t           *dv;\r
-       vec3_t                          texX, texY;\r
-       vec_t                           x, y;\r
-       vec3_t                          vTranslated;\r
-       qboolean                        indexed;\r
-       byte                            shaderIndexes[ 256 ];\r
-       float                           offsets[ 256 ];\r
-       char                            tempShader[ MAX_QPATH ];\r
-\r
-       \r
-       /* ydnar: don't make a drawsurf for culled sides */\r
-       if( s->culled )\r
-               return NULL;\r
-       \r
-       /* range check */\r
-       if( w->numpoints > MAX_POINTS_ON_WINDING )\r
-               Error( "DrawSurfaceForSide: w->numpoints = %d (> %d)", w->numpoints, MAX_POINTS_ON_WINDING );\r
-       \r
-       /* get shader */\r
-       si = s->shaderInfo;\r
-       \r
-       /* ydnar: gs mods: check for indexed shader */\r
-       if( si->indexed && b->im != NULL )\r
-       {\r
-               /* indexed */\r
-               indexed = qtrue;\r
-               \r
-               /* get shader indexes for each point */\r
-               for( i = 0; i < w->numpoints; i++ )\r
-               {\r
-                       shaderIndexes[ i ] = GetShaderIndexForPoint( b->im, b->eMins, b->eMaxs, w->p[ i ] );\r
-                       offsets[ i ] = b->im->offsets[ shaderIndexes[ i ] ];\r
-                       //%     Sys_Printf( "%f ", offsets[ i ] );\r
-               }\r
-               \r
-               /* get matching shader and set alpha */\r
-               parent = si;\r
-               si = GetIndexedShader( parent, b->im, w->numpoints, shaderIndexes );\r
-       }\r
-       else\r
-               indexed = qfalse;\r
-       \r
-       /* ydnar: sky hack/fix for GL_CLAMP borders on ati cards */\r
-       if( skyFixHack && si->skyParmsImageBase[ 0 ] != '\0' )\r
-       {\r
-               //%     Sys_FPrintf( SYS_VRB, "Enabling sky hack for shader %s using env %s\n", si->shader, si->skyParmsImageBase );\r
-               sprintf( tempShader, "%s_lf", si->skyParmsImageBase );\r
-               DrawSurfaceForShader( tempShader );\r
-               sprintf( tempShader, "%s_rt", si->skyParmsImageBase );\r
-               DrawSurfaceForShader( tempShader );\r
-               sprintf( tempShader, "%s_ft", si->skyParmsImageBase );\r
-               DrawSurfaceForShader( tempShader );\r
-               sprintf( tempShader, "%s_bk", si->skyParmsImageBase );\r
-               DrawSurfaceForShader( tempShader );\r
-               sprintf( tempShader, "%s_up", si->skyParmsImageBase );\r
-               DrawSurfaceForShader( tempShader );\r
-               sprintf( tempShader, "%s_dn", si->skyParmsImageBase );\r
-               DrawSurfaceForShader( tempShader );\r
-       }\r
-       \r
-       /* ydnar: gs mods */\r
-       ds = AllocDrawSurface( SURFACE_FACE );\r
-       ds->entityNum = b->entityNum;\r
-       ds->castShadows = b->castShadows;\r
-       ds->recvShadows = b->recvShadows;\r
-       \r
-       ds->planar = qtrue;\r
-       ds->planeNum = s->planenum;\r
-       VectorCopy( mapplanes[ s->planenum ].normal, ds->lightmapVecs[ 2 ] );\r
-       \r
-       ds->shaderInfo = si;\r
-       ds->mapBrush = b;\r
-       ds->sideRef = AllocSideRef( s, NULL );\r
-       ds->fogNum = -1;\r
-       ds->lightmapScale = b->lightmapScale;\r
-       ds->numVerts = w->numpoints;\r
-       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
-       memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );\r
-       \r
-       /* compute s/t coordinates from brush primitive texture matrix (compute axis base) */\r
-       ComputeAxisBase( mapplanes[ s->planenum ].normal, texX, texY );\r
-       \r
-       /* create the vertexes */\r
-       for( j = 0; j < w->numpoints; j++ )\r
-       {\r
-               /* get the drawvert */\r
-               dv = ds->verts + j;\r
-               \r
-               /* copy xyz and do potential z offset */\r
-               VectorCopy( w->p[ j ], dv->xyz );\r
-               if( indexed )\r
-                       dv->xyz[ 2 ] += offsets[ j ];\r
-               \r
-               /* round the xyz to a given precision and translate by origin */\r
-               for( i = 0 ; i < 3 ; i++ )\r
-                       dv->xyz[ i ] = SNAP_INT_TO_FLOAT * floor( dv->xyz[ i ] * SNAP_FLOAT_TO_INT + 0.5f );\r
-               VectorAdd( dv->xyz, e->origin, vTranslated );\r
-               \r
-               /* ydnar: tek-fu celshading support for flat shaded shit */\r
-               if( flat )\r
-               {\r
-                       dv->st[ 0 ] = si->stFlat[ 0 ];\r
-                       dv->st[ 1 ] = si->stFlat[ 1 ];\r
-               }\r
-               \r
-               /* ydnar: gs mods: added support for explicit shader texcoord generation */\r
-               else if( si->tcGen )\r
-               {\r
-                       dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], vTranslated );\r
-                       dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], vTranslated );\r
-               }\r
-               \r
-               /* old quake-style texturing */\r
-               else if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
-               {\r
-                       /* nearest-axial projection */\r
-                       dv->st[ 0 ] = s->vecs[ 0 ][ 3 ] + DotProduct( s->vecs[ 0 ], vTranslated );\r
-                       dv->st[ 1 ] = s->vecs[ 1 ][ 3 ] + DotProduct( s->vecs[ 1 ], vTranslated );\r
-                       dv->st[ 0 ] /= si->shaderWidth;\r
-                       dv->st[ 1 ] /= si->shaderHeight;\r
-               }\r
-               \r
-               /* brush primitive texturing */\r
-               else\r
-               {\r
-                       /* calculate texture s/t from brush primitive texture matrix */\r
-                       x = DotProduct( vTranslated, texX );\r
-                       y = DotProduct( vTranslated, texY );\r
-                       dv->st[ 0 ] = s->texMat[ 0 ][ 0 ] * x + s->texMat[ 0 ][ 1 ] * y + s->texMat[ 0 ][ 2 ];\r
-                       dv->st[ 1 ] = s->texMat[ 1 ][ 0 ] * x + s->texMat[ 1 ][ 1 ] * y + s->texMat[ 1 ][ 2 ];\r
-               }\r
-               \r
-               /* copy normal */\r
-               VectorCopy( mapplanes[ s->planenum ].normal, dv->normal );\r
-               \r
-               /* ydnar: set color */\r
-               for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-               {\r
-                       dv->color[ k ][ 0 ] = 255;\r
-                       dv->color[ k ][ 1 ] = 255;\r
-                       dv->color[ k ][ 2 ] = 255;\r
-                       \r
-                       /* ydnar: gs mods: handle indexed shader blending */\r
-                       dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ j ] : 255);\r
-               }\r
-       }\r
-       \r
-       /* set cel shader */\r
-       ds->celShader = b->celShader;\r
-       \r
-       /* finish surface */\r
-       FinishSurface( ds );\r
-       \r
-       /* ydnar: gs mods: moved st biasing elsewhere */\r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-DrawSurfaceForMesh()\r
-moved here from patch.c\r
-*/\r
-\r
-#define YDNAR_NORMAL_EPSILON 0.50f\r
-\r
-qboolean VectorCompareExt( vec3_t n1, vec3_t n2, float epsilon )\r
-{\r
-       int             i;\r
-       \r
-       \r
-       /* test */\r
-       for( i= 0; i < 3; i++ )\r
-               if( fabs( n1[ i ] - n2[ i ]) > epsilon )\r
-                       return qfalse;\r
-       return qtrue;\r
-}\r
-\r
-mapDrawSurface_t *DrawSurfaceForMesh( entity_t *e, parseMesh_t *p, mesh_t *mesh )\r
-{\r
-       int                                     i, k, numVerts;\r
-       vec4_t                          plane;\r
-       qboolean                        planar;\r
-       float                           dist;\r
-       mapDrawSurface_t        *ds;\r
-       shaderInfo_t            *si, *parent;\r
-       bspDrawVert_t           *dv;\r
-       vec3_t                          vTranslated;\r
-       mesh_t                          *copy;\r
-       qboolean                        indexed;\r
-       byte                            shaderIndexes[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];\r
-       float                           offsets[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];\r
-       \r
-       \r
-       /* get mesh and shader shader */\r
-       if( mesh == NULL )\r
-               mesh = &p->mesh;\r
-       si = p->shaderInfo;\r
-       if( mesh == NULL || si == NULL )\r
-               return NULL;\r
-       \r
-       /* get vertex count */\r
-       numVerts = mesh->width * mesh->height;\r
-       \r
-       /* to make valid normals for patches with degenerate edges,\r
-          we need to make a copy of the mesh and put the aproximating\r
-          points onto the curve */\r
-       \r
-       /* create a copy of the mesh */\r
-       copy = CopyMesh( mesh );\r
-       \r
-       /* store off the original (potentially bad) normals */\r
-       MakeMeshNormals( *copy );\r
-       for( i = 0; i < numVerts; i++ )\r
-               VectorCopy( copy->verts[ i ].normal, mesh->verts[ i ].normal );\r
-       \r
-       /* put the mesh on the curve */\r
-       PutMeshOnCurve( *copy );\r
-\r
-       /* find new normals (to take into account degenerate/flipped edges */\r
-       MakeMeshNormals( *copy );\r
-       for( i = 0; i < numVerts; i++ )\r
-       {\r
-               /* ydnar: only copy normals that are significantly different from the originals */\r
-               if( DotProduct( copy->verts[ i ].normal, mesh->verts[ i ].normal ) < 0.75f )\r
-                       VectorCopy( copy->verts[ i ].normal, mesh->verts[ i ].normal );\r
-       }\r
-       \r
-       /* free the old mesh */\r
-       FreeMesh( copy );\r
-       \r
-       /* ydnar: gs mods: check for indexed shader */\r
-       if( si->indexed && p->im != NULL )\r
-       {\r
-               /* indexed */\r
-               indexed = qtrue;\r
-\r
-               /* get shader indexes for each point */\r
-               for( i = 0; i < numVerts; i++ )\r
-               {\r
-                       shaderIndexes[ i ] = GetShaderIndexForPoint( p->im, p->eMins, p->eMaxs, mesh->verts[ i ].xyz );\r
-                       offsets[ i ] = p->im->offsets[ shaderIndexes[ i ] ];\r
-               }\r
-               \r
-               /* get matching shader and set alpha */\r
-               parent = si;\r
-               si = GetIndexedShader( parent, p->im, numVerts, shaderIndexes );\r
-       }\r
-       else\r
-               indexed = qfalse;\r
-       \r
-       \r
-       /* ydnar: gs mods */\r
-       ds = AllocDrawSurface( SURFACE_PATCH );\r
-       ds->entityNum = p->entityNum;\r
-       ds->castShadows = p->castShadows;\r
-       ds->recvShadows = p->recvShadows;\r
-       \r
-       ds->shaderInfo = si;\r
-       ds->mapMesh = p;\r
-       ds->lightmapScale = p->lightmapScale;   /* ydnar */\r
-       ds->patchWidth = mesh->width;\r
-       ds->patchHeight = mesh->height;\r
-       ds->numVerts = ds->patchWidth * ds->patchHeight;\r
-       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
-       memcpy( ds->verts, mesh->verts, ds->numVerts * sizeof( *ds->verts ) );\r
-       \r
-       ds->fogNum = -1;\r
-       ds->planeNum = -1;\r
-       \r
-       ds->longestCurve = p->longestCurve;\r
-       ds->maxIterations = p->maxIterations;\r
-       \r
-       /* construct a plane from the first vert */\r
-       VectorCopy( mesh->verts[ 0 ].normal, plane );\r
-       plane[ 3 ] = DotProduct( mesh->verts[ 0 ].xyz, plane );\r
-       planar = qtrue;\r
-       \r
-       /* spew forth errors */\r
-       if( VectorLength( plane ) < 0.001f )\r
-               Sys_Printf( "BOGUS " );\r
-       \r
-       /* test each vert */\r
-       for( i = 1; i < ds->numVerts && planar; i++ )\r
-       {\r
-               /* normal test */\r
-               if( VectorCompare( plane, mesh->verts[ i ].normal ) == qfalse )\r
-                       planar = qfalse;\r
-               \r
-               /* point-plane test */\r
-               dist = DotProduct( mesh->verts[ i ].xyz, plane ) - plane[ 3 ];\r
-               if( fabs( dist ) > EQUAL_EPSILON )\r
-                       planar = qfalse;\r
-       }\r
-       \r
-       /* add a map plane */\r
-       if( planar )\r
-       {\r
-               /* make a map plane */\r
-               ds->planeNum = FindFloatPlane( plane, plane[ 3 ], 1, &mesh->verts[ 0 ].xyz );\r
-               VectorCopy( plane, ds->lightmapVecs[ 2 ] );\r
-               \r
-               /* push this normal to all verts (ydnar 2003-02-14: bad idea, small patches get screwed up) */\r
-               for( i = 0; i < ds->numVerts; i++ )\r
-                       VectorCopy( plane, ds->verts[ i ].normal );\r
-       }\r
-       \r
-       /* walk the verts to do special stuff */\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-       {\r
-               /* get the drawvert */\r
-               dv = &ds->verts[ i ];\r
-               \r
-               /* ydnar: tek-fu celshading support for flat shaded shit */\r
-               if( flat )\r
-               {\r
-                       dv->st[ 0 ] = si->stFlat[ 0 ];\r
-                       dv->st[ 1 ] = si->stFlat[ 1 ];\r
-               }\r
-               \r
-               /* ydnar: gs mods: added support for explicit shader texcoord generation */\r
-               else if( si->tcGen )\r
-               {\r
-                       /* translate by origin and project the texture */\r
-                       VectorAdd( dv->xyz, e->origin, vTranslated );\r
-                       dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], vTranslated );\r
-                       dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], vTranslated );\r
-               }\r
-               \r
-               /* ydnar: set color */\r
-               for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-               {\r
-                       dv->color[ k ][ 0 ] = 255;\r
-                       dv->color[ k ][ 1 ] = 255;\r
-                       dv->color[ k ][ 2 ] = 255;\r
-                       \r
-                       /* ydnar: gs mods: handle indexed shader blending */\r
-                       dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ i ] : 255);\r
-               }\r
-               \r
-               /* ydnar: offset */\r
-               if( indexed )\r
-                       dv->xyz[ 2 ] += offsets[ i ];\r
-       }\r
-       \r
-       /* set cel shader */\r
-       ds->celShader = p->celShader;\r
-       \r
-       /* finish surface */\r
-       FinishSurface( ds );\r
-       \r
-       /* return the drawsurface */\r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-DrawSurfaceForFlare() - ydnar\r
-creates a flare draw surface\r
-*/\r
-\r
-mapDrawSurface_t *DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, char *flareShader, int lightStyle )\r
-{\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* emit flares? */\r
-       if( emitFlares == qfalse )\r
-               return NULL;\r
-       \r
-       /* allocate drawsurface */\r
-       ds = AllocDrawSurface( SURFACE_FLARE );\r
-       ds->entityNum = entNum;\r
-       \r
-       /* set it up */\r
-       if( flareShader != NULL && flareShader[ 0 ] != '\0' )\r
-               ds->shaderInfo = ShaderInfoForShader( flareShader );\r
-       else\r
-               ds->shaderInfo = ShaderInfoForShader( game->flareShader );\r
-       if( origin != NULL )\r
-               VectorCopy( origin, ds->lightmapOrigin );\r
-       if( normal != NULL )\r
-               VectorCopy( normal, ds->lightmapVecs[ 2 ] );\r
-       if( color != NULL )\r
-               VectorCopy( color, ds->lightmapVecs[ 0 ] );\r
-       \r
-       /* store light style */\r
-       ds->lightStyle = lightStyle;\r
-       if( ds->lightStyle < 0 || ds->lightStyle >= LS_NONE )\r
-               ds->lightStyle = LS_NORMAL;\r
-       \r
-       /* fixme: fog */\r
-       \r
-       /* return to sender */\r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-DrawSurfaceForShader() - ydnar\r
-creates a bogus surface to forcing the game to load a shader\r
-*/\r
-\r
-mapDrawSurface_t *DrawSurfaceForShader( char *shader )\r
-{\r
-       int                                     i;\r
-       shaderInfo_t            *si;\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* get shader */\r
-       si = ShaderInfoForShader( shader );\r
-\r
-       /* find existing surface */\r
-       for( i = 0; i < numMapDrawSurfs; i++ )\r
-       {\r
-               /* get surface */\r
-               ds = &mapDrawSurfs[ i ];\r
-               \r
-               /* check it */\r
-               if( ds->shaderInfo == si )\r
-                       return ds;\r
-       }\r
-       \r
-       /* create a new surface */\r
-       ds = AllocDrawSurface( SURFACE_SHADER );\r
-       ds->entityNum = 0;\r
-       ds->shaderInfo = ShaderInfoForShader( shader );\r
-       \r
-       /* return to sender */\r
-       return ds;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddSurfaceFlare() - ydnar\r
-creates flares (coronas) centered on surfaces\r
-*/\r
-\r
-static void AddSurfaceFlare( mapDrawSurface_t *ds, vec3_t entityOrigin )\r
-{\r
-       vec3_t                          origin;\r
-       int                                     i;\r
-       \r
-       \r
-       /* find centroid */\r
-       VectorClear( origin );\r
-       for ( i = 0; i < ds->numVerts; i++ )\r
-               VectorAdd( origin, ds->verts[ i ].xyz, origin );\r
-       VectorScale( origin, (1.0f / ds->numVerts), origin );\r
-       if( entityOrigin != NULL )\r
-               VectorAdd( origin, entityOrigin, origin );\r
-       \r
-       /* push origin off surface a bit */\r
-       VectorMA( origin, 2.0f,  ds->lightmapVecs[ 2 ], origin );\r
-       \r
-       /* create the drawsurface */\r
-       DrawSurfaceForFlare( ds->entityNum, origin, ds->lightmapVecs[ 2 ], ds->shaderInfo->color, ds->shaderInfo->flareShader, ds->shaderInfo->lightStyle );\r
-}\r
-\r
-\r
-\r
-/*\r
-SubdivideFace()\r
-subdivides a face surface until it is smaller than the specified size (subdivisions)\r
-*/\r
-\r
-static void SubdivideFace( entity_t *e, brush_t *brush, side_t *side, winding_t *w, int fogNum, float subdivisions )\r
-{\r
-       int                                     i;\r
-       int                                     axis;\r
-       vec3_t                          bounds[ 2 ];\r
-       const float                     epsilon = 0.1;\r
-       int                                     subFloor, subCeil;\r
-       winding_t                       *frontWinding, *backWinding;\r
-       mapDrawSurface_t        *ds;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( w == NULL )\r
-               return;\r
-       if( w->numpoints < 3 )\r
-               Error( "SubdivideFaceSurface: Bad w->numpoints" );\r
-       \r
-       /* determine surface bounds */\r
-       ClearBounds( bounds[ 0 ], bounds[ 1 ] );\r
-       for( i = 0; i < w->numpoints; i++ )\r
-               AddPointToBounds( w->p[ i ], bounds[ 0 ], bounds[ 1 ] );\r
-       \r
-       /* split the face */\r
-       for( axis = 0; axis < 3; axis++ )\r
-       {\r
-               vec3_t                  planePoint = { 0, 0, 0 };\r
-               vec3_t                  planeNormal = { 0, 0, 0 };\r
-               float                   d;\r
-               \r
-               \r
-               /* create an axial clipping plane */\r
-               subFloor = floor( bounds[ 0 ][ axis ] / subdivisions) * subdivisions;\r
-               subCeil = ceil( bounds[ 1 ][ axis ] / subdivisions) * subdivisions;\r
-               planePoint[ axis ] = subFloor + subdivisions;\r
-               planeNormal[ axis ] = -1;\r
-               d = DotProduct( planePoint, planeNormal );\r
-\r
-               /* subdivide if necessary */\r
-               if( (subCeil - subFloor) > subdivisions )\r
-               {\r
-                       /* clip the winding */\r
-                       ClipWindingEpsilon( w, planeNormal, d, epsilon, &frontWinding, &backWinding );\r
-\r
-                       /* the clip may not produce two polygons if it was epsilon close */\r
-                       if( frontWinding == NULL )\r
-                               w = backWinding;\r
-                       else if( backWinding == NULL )\r
-                               w = frontWinding;\r
-                       else\r
-                       {\r
-                               SubdivideFace( e, brush, side, frontWinding, fogNum, subdivisions );\r
-                               SubdivideFace( e, brush, side, backWinding, fogNum, subdivisions );\r
-                               return;\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* create a face surface */\r
-       ds = DrawSurfaceForSide( e, brush, side, w );\r
-       \r
-       /* set correct fog num */\r
-       ds->fogNum = fogNum;\r
-}\r
-\r
-\r
-\r
-/*\r
-SubdivideFaceSurfaces()\r
-chop up brush face surfaces that have subdivision attributes\r
-ydnar: and subdivide surfaces that exceed specified texture coordinate range\r
-*/\r
-\r
-void SubdivideFaceSurfaces( entity_t *e, tree_t *tree )\r
-{\r
-       int                                     i, j, numBaseDrawSurfs, fogNum;\r
-       mapDrawSurface_t        *ds;\r
-       brush_t                         *brush;\r
-       side_t                          *side;\r
-       shaderInfo_t            *si;\r
-       winding_t                       *w;\r
-       float                           range, size, subdivisions, s2;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- SubdivideFaceSurfaces ---\n" );\r
-       \r
-       /* walk the list of surfaces */\r
-       numBaseDrawSurfs = numMapDrawSurfs;\r
-       for( i = e->firstDrawSurf; i < numBaseDrawSurfs; i++ )\r
-       {\r
-               /* get surface */\r
-               ds = &mapDrawSurfs[ i ];\r
-\r
-               /* only subdivide brush sides */\r
-               if( ds->type != SURFACE_FACE || ds->mapBrush == NULL || ds->sideRef == NULL || ds->sideRef->side == NULL )\r
-                       continue;\r
-               \r
-               /* get bits */\r
-               brush = ds->mapBrush;\r
-               side = ds->sideRef->side;\r
-               \r
-               /* check subdivision for shader */\r
-               si = side->shaderInfo;\r
-               if( si == NULL )\r
-                       continue;\r
-               \r
-               /* ydnar: don't subdivide sky surfaces */\r
-               if( si->compileFlags & C_SKY )\r
-                       continue;\r
-               \r
-               /* do texture coordinate range check */\r
-               ClassifySurfaces( 1, ds );\r
-               if( CalcSurfaceTextureRange( ds ) == qfalse )\r
-               {\r
-                       /* calculate subdivisions texture range (this code is shit) */\r
-                       range = (ds->texRange[ 0 ] > ds->texRange[ 1 ] ? ds->texRange[ 0 ] : ds->texRange[ 1 ]);\r
-                       size = ds->maxs[ 0 ] - ds->mins[ 0 ];\r
-                       for( j = 1; j < 3; j++ )\r
-                               if( (ds->maxs[ j ] - ds->mins[ j ]) > size )\r
-                                       size = ds->maxs[ j ] - ds->mins[ j ];\r
-                       subdivisions = (size / range) * texRange;\r
-                       subdivisions = ceil( subdivisions / 2 ) * 2;\r
-                       for( j = 1; j < 8; j++ )\r
-                       {\r
-                               s2 = ceil( (float) texRange / j );\r
-                               if( fabs( subdivisions - s2 ) <= 4.0 )\r
-                               {\r
-                                       subdivisions = s2;\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-               else\r
-                       subdivisions = si->subdivisions;\r
-               \r
-               /* get subdivisions from shader */\r
-               if(     si->subdivisions > 0 && si->subdivisions < subdivisions )\r
-                       subdivisions = si->subdivisions;\r
-               if( subdivisions < 1.0f )\r
-                       continue;\r
-               \r
-               /* preserve fog num */\r
-               fogNum = ds->fogNum;\r
-               \r
-               /* make a winding and free the surface */\r
-               w = WindingFromDrawSurf( ds );\r
-               ClearSurface( ds );\r
-               \r
-               /* subdivide it */\r
-               SubdivideFace( e, brush, side, w, fogNum, subdivisions );\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-====================\r
-ClipSideIntoTree_r\r
-\r
-Adds non-opaque leaf fragments to the convex hull\r
-====================\r
-*/\r
-\r
-void ClipSideIntoTree_r( winding_t *w, side_t *side, node_t *node )\r
-{\r
-       plane_t                 *plane;\r
-       winding_t               *front, *back;\r
-\r
-       if ( !w ) {\r
-               return;\r
-       }\r
-\r
-       if ( node->planenum != PLANENUM_LEAF ) {\r
-               if ( side->planenum == node->planenum ) {\r
-                       ClipSideIntoTree_r( w, side, node->children[0] );\r
-                       return;\r
-               }\r
-               if ( side->planenum == ( node->planenum ^ 1) ) {\r
-                       ClipSideIntoTree_r( w, side, node->children[1] );\r
-                       return;\r
-               }\r
-\r
-               plane = &mapplanes[ node->planenum ];\r
-               ClipWindingEpsilon ( w, plane->normal, plane->dist,\r
-                               ON_EPSILON, &front, &back );\r
-               FreeWinding( w );\r
-\r
-               ClipSideIntoTree_r( front, side, node->children[0] );\r
-               ClipSideIntoTree_r( back, side, node->children[1] );\r
-\r
-               return;\r
-       }\r
-\r
-       // if opaque leaf, don't add\r
-       if ( !node->opaque ) {\r
-               AddWindingToConvexHull( w, &side->visibleHull, mapplanes[ side->planenum ].normal );\r
-       }\r
-\r
-       FreeWinding( w );\r
-       return;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-static int g_numHiddenFaces, g_numCoinFaces;\r
-\r
-\r
-\r
-/*\r
-CullVectorCompare() - ydnar\r
-compares two vectors with an epsilon\r
-*/\r
-\r
-#define CULL_EPSILON 0.1f\r
-\r
-qboolean CullVectorCompare( const vec3_t v1, const vec3_t v2 )\r
-{\r
-       int             i;\r
-       \r
-       \r
-       for( i = 0; i < 3; i++ )\r
-               if( fabs( v1[ i ] - v2[ i ] ) > CULL_EPSILON )\r
-                       return qfalse;\r
-       return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-SideInBrush() - ydnar\r
-determines if a brushside lies inside another brush\r
-*/\r
-\r
-qboolean SideInBrush( side_t *side, brush_t *b )\r
-{\r
-       int                     i, s;\r
-       plane_t         *plane;\r
-       \r
-       \r
-       /* ignore sides w/o windings or shaders */\r
-       if( side->winding == NULL || side->shaderInfo == NULL )\r
-               return qtrue;\r
-\r
-       /* ignore culled sides and translucent brushes */\r
-       if( side->culled == qtrue || (b->compileFlags & C_TRANSLUCENT) )\r
-               return qfalse;\r
-\r
-       /* side iterator */\r
-       for( i = 0; i < b->numsides; i++ )\r
-       {\r
-               /* fail if any sides are caulk */\r
-               if( b->sides[ i ].compileFlags & C_NODRAW )\r
-                       return qfalse;\r
-\r
-               /* check if side's winding is on or behind the plane */\r
-               plane = &mapplanes[ b->sides[ i ].planenum ];\r
-               s = WindingOnPlaneSide( side->winding, plane->normal, plane->dist );\r
-               if( s == SIDE_FRONT || s == SIDE_CROSS )\r
-                       return qfalse;\r
-       }\r
-       \r
-       /* don't cull autosprite or polygonoffset surfaces */\r
-       if( side->shaderInfo )\r
-       {\r
-               if( side->shaderInfo->autosprite || side->shaderInfo->polygonOffset )\r
-                       return qfalse;\r
-       }\r
-       \r
-       /* inside */\r
-       side->culled = qtrue;\r
-       g_numHiddenFaces++;\r
-       return qtrue;\r
-}\r
-\r
-\r
-/*\r
-CullSides() - ydnar\r
-culls obscured or buried brushsides from the map\r
-*/\r
-\r
-void CullSides( entity_t *e )\r
-{\r
-       int                     numPoints;\r
-       int                     i, j, k, l, first, second, dir;\r
-       winding_t       *w1, *w2;\r
-       brush_t *b1, *b2;\r
-       side_t          *side1, *side2;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- CullSides ---\n" );\r
-       \r
-       g_numHiddenFaces = 0;\r
-       g_numCoinFaces = 0;\r
-       \r
-       /* brush interator 1 */\r
-       for( b1 = e->brushes; b1; b1 = b1->next )\r
-       {\r
-               /* sides check */\r
-               if( b1->numsides < 1 )\r
-                       continue;\r
-\r
-               /* brush iterator 2 */\r
-               for( b2 = b1->next; b2; b2 = b2->next )\r
-               {\r
-                       /* sides check */\r
-                       if( b2->numsides < 1 )\r
-                               continue;\r
-                       \r
-                       /* original check */\r
-                       if( b1->original == b2->original && b1->original != NULL )\r
-                               continue;\r
-                       \r
-                       /* bbox check */\r
-                       j = 0;\r
-                       for( i = 0; i < 3; i++ )\r
-                               if( b1->mins[ i ] > b2->maxs[ i ] || b1->maxs[ i ] < b2->mins[ i ] )\r
-                                       j++;\r
-                       if( j )\r
-                               continue;\r
-\r
-                       /* cull inside sides */\r
-                       for( i = 0; i < b1->numsides; i++ )\r
-                               SideInBrush( &b1->sides[ i ], b2 );\r
-                       for( i = 0; i < b2->numsides; i++ )\r
-                               SideInBrush( &b2->sides[ i ], b1 );\r
-                       \r
-                       /* side iterator 1 */\r
-                       for( i = 0; i < b1->numsides; i++ )\r
-                       {\r
-                               /* winding check */\r
-                               side1 = &b1->sides[ i ];\r
-                               w1 = side1->winding;\r
-                               if( w1 == NULL )\r
-                                       continue;\r
-                               numPoints = w1->numpoints;\r
-                               if( side1->shaderInfo == NULL )\r
-                                       continue;\r
-                               \r
-                               /* side iterator 2 */\r
-                               for( j = 0; j < b2->numsides; j++ )\r
-                               {\r
-                                       /* winding check */\r
-                                       side2 = &b2->sides[ j ];\r
-                                       w2 = side2->winding;\r
-                                       if( w2 == NULL )\r
-                                               continue;\r
-                                       if( side2->shaderInfo == NULL )\r
-                                               continue;\r
-                                       if( w1->numpoints != w2->numpoints )\r
-                                               continue;\r
-                                       if( side1->culled == qtrue && side2->culled == qtrue )\r
-                                               continue;\r
-                                       \r
-                                       /* compare planes */\r
-                                       if( (side1->planenum & ~0x00000001) != (side2->planenum & ~0x00000001) )\r
-                                               continue;\r
-                                       \r
-                                       /* get autosprite and polygonoffset status */\r
-                                       if( side1->shaderInfo &&\r
-                                               (side1->shaderInfo->autosprite || side1->shaderInfo->polygonOffset) )\r
-                                               continue;\r
-                                       if( side2->shaderInfo &&\r
-                                               (side2->shaderInfo->autosprite || side2->shaderInfo->polygonOffset) )\r
-                                               continue;\r
-                                       \r
-                                       /* find first common point */\r
-                                       first = -1;\r
-                                       for( k = 0; k < numPoints; k++ )\r
-                                       {\r
-                                               if( VectorCompare( w1->p[ 0 ], w2->p[ k ] ) )\r
-                                               {\r
-                                                       first = k;\r
-                                                       k = numPoints;\r
-                                               }\r
-                                       }\r
-                                       if( first == -1 )\r
-                                               continue;\r
-                                       \r
-                                       /* find second common point (regardless of winding order) */\r
-                                       second = -1;\r
-                                       dir = 0;\r
-                                       if( (first + 1) < numPoints )\r
-                                               second = first + 1;\r
-                                       else\r
-                                               second = 0;\r
-                                       if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )\r
-                                               dir = 1;\r
-                                       else\r
-                                       {\r
-                                               if( first > 0 )\r
-                                                       second = first - 1;\r
-                                               else\r
-                                                       second = numPoints - 1;\r
-                                               if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )\r
-                                                       dir = -1;\r
-                                       }\r
-                                       if( dir == 0 )\r
-                                               continue;\r
-                                       \r
-                                       /* compare the rest of the points */\r
-                                       l = first;\r
-                                       for( k = 0; k < numPoints; k++ )\r
-                                       {\r
-                                               if( !CullVectorCompare( w1->p[ k ], w2->p[ l ] ) )\r
-                                                       k = 100000;\r
-                                               \r
-                                               l += dir;\r
-                                               if( l < 0 )\r
-                                                       l = numPoints - 1;\r
-                                               else if( l >= numPoints )\r
-                                                       l = 0;\r
-                                       }\r
-                                       if( k >= 100000 )\r
-                                               continue;\r
-                                       \r
-                                       /* cull face 1 */\r
-                                       if( !side2->culled && !(side2->compileFlags & C_TRANSLUCENT) && !(side2->compileFlags & C_NODRAW) )\r
-                                       {\r
-                                               side1->culled = qtrue;\r
-                                               g_numCoinFaces++;\r
-                                       }\r
-                                       \r
-                                       if( side1->planenum == side2->planenum && side1->culled == qtrue )\r
-                                               continue;\r
-                                       \r
-                                       /* cull face 2 */\r
-                                       if( !side1->culled && !(side1->compileFlags & C_TRANSLUCENT) && !(side1->compileFlags & C_NODRAW) )\r
-                                       {\r
-                                               side2->culled = qtrue;\r
-                                               g_numCoinFaces++;\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* emit some stats */\r
-       Sys_FPrintf( SYS_VRB, "%9d hidden faces culled\n", g_numHiddenFaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d coincident faces culled\n", g_numCoinFaces );\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-ClipSidesIntoTree()\r
-\r
-creates side->visibleHull for all visible sides\r
-\r
-the drawsurf for a side will consist of the convex hull of\r
-all points in non-opaque clusters, which allows overlaps\r
-to be trimmed off automatically.\r
-*/\r
-\r
-void ClipSidesIntoTree( entity_t *e, tree_t *tree )\r
-{\r
-       brush_t         *b;\r
-       int                             i;\r
-       winding_t               *w;\r
-       side_t                  *side, *newSide;\r
-       shaderInfo_t    *si;\r
-  \r
-       \r
-       /* ydnar: cull brush sides */\r
-       CullSides( e );\r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- ClipSidesIntoTree ---\n" );\r
-       \r
-       /* walk the brush list */\r
-       for( b = e->brushes; b; b = b->next )\r
-       {\r
-               /* walk the brush sides */\r
-               for( i = 0; i < b->numsides; i++ )\r
-               {\r
-                       /* get side */\r
-                       side = &b->sides[ i ];\r
-                       if( side->winding == NULL )\r
-                               continue;\r
-                       \r
-                       /* copy the winding */\r
-                       w = CopyWinding( side->winding );\r
-                       side->visibleHull = NULL;\r
-                       ClipSideIntoTree_r( w, side, tree->headnode );\r
-                       \r
-                       /* anything left? */\r
-                       w = side->visibleHull;\r
-                       if( w == NULL )\r
-                               continue;\r
-                       \r
-                       /* shader? */\r
-                       si = side->shaderInfo;\r
-                       if( si == NULL )\r
-                               continue;\r
-                       \r
-                       /* don't create faces for non-visible sides */\r
-                       /* ydnar: except indexed shaders, like common/terrain and nodraw fog surfaces */\r
-                       if( (si->compileFlags & C_NODRAW) && si->indexed == qfalse && !(si->compileFlags & C_FOG) )\r
-                               continue;\r
-                       \r
-                       /* always use the original winding for autosprites and noclip faces */\r
-                       if( si->autosprite || si->noClip )\r
-                               w = side->winding;\r
-                       \r
-                       /* save this winding as a visible surface */\r
-                       DrawSurfaceForSide( e, b, side, w );\r
-\r
-                       /* make a back side for fog */\r
-                       if( !(si->compileFlags & C_FOG) )\r
-                               continue;\r
-                       \r
-                       /* duplicate the up-facing side */\r
-                       w = ReverseWinding( w );\r
-                       newSide = safe_malloc( sizeof( *side ) );\r
-                       *newSide = *side;\r
-                       newSide->visibleHull = w;\r
-                       newSide->planenum ^= 1;\r
-                       \r
-                       /* save this winding as a visible surface */\r
-                       DrawSurfaceForSide( e, b, newSide, w );\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-\r
-this section deals with filtering drawsurfaces into the bsp tree,\r
-adding references to each leaf a surface touches\r
-\r
-*/\r
-\r
-/*\r
-AddReferenceToLeaf() - ydnar\r
-adds a reference to surface ds in the bsp leaf node\r
-*/\r
-\r
-int AddReferenceToLeaf( mapDrawSurface_t *ds, node_t *node )\r
-{\r
-       drawSurfRef_t   *dsr;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( node->planenum != PLANENUM_LEAF || node->opaque )\r
-               return 0;\r
-       \r
-       /* try to find an existing reference */\r
-       for( dsr = node->drawSurfReferences; dsr; dsr = dsr->nextRef )\r
-       {\r
-               if( dsr->outputNum == numBSPDrawSurfaces )\r
-                       return 0;\r
-       }\r
-       \r
-       /* add a new reference */\r
-       dsr = safe_malloc( sizeof( *dsr ) );\r
-       dsr->outputNum = numBSPDrawSurfaces;\r
-       dsr->nextRef = node->drawSurfReferences;\r
-       node->drawSurfReferences = dsr;\r
-       \r
-       /* ydnar: sky/skybox surfaces */\r
-       if( node->skybox )\r
-               ds->skybox = qtrue;\r
-       if( ds->shaderInfo->compileFlags & C_SKY )\r
-               node->sky = qtrue;\r
-       \r
-       /* return */\r
-       return 1;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddReferenceToTree_r() - ydnar\r
-adds a reference to the specified drawsurface to every leaf in the tree\r
-*/\r
-\r
-int AddReferenceToTree_r( mapDrawSurface_t *ds, node_t *node, qboolean skybox )\r
-{\r
-       int             i, refs = 0;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( node == NULL )\r
-               return 0;\r
-       \r
-       /* is this a decision node? */\r
-       if( node->planenum != PLANENUM_LEAF )\r
-       {\r
-               /* add to child nodes and return */\r
-               refs += AddReferenceToTree_r( ds, node->children[ 0 ], skybox );\r
-               refs += AddReferenceToTree_r( ds, node->children[ 1 ], skybox );\r
-               return refs;\r
-       }\r
-       \r
-       /* ydnar */\r
-       if( skybox )\r
-       {\r
-               /* skybox surfaces only get added to sky leaves */\r
-               if( !node->sky )\r
-                       return 0;\r
-               \r
-               /* increase the leaf bounds */\r
-               for( i = 0; i < ds->numVerts; i++ )\r
-                       AddPointToBounds( ds->verts[ i ].xyz, node->mins, node->maxs );\r
-       }\r
-       \r
-       /* add a reference */\r
-       return AddReferenceToLeaf( ds, node );\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterPointIntoTree_r() - ydnar\r
-filters a single point from a surface into the tree\r
-*/\r
-\r
-int FilterPointIntoTree_r( vec3_t point, mapDrawSurface_t *ds, node_t *node )\r
-{\r
-       float                   d;\r
-       plane_t                 *plane;\r
-       int                             refs = 0;\r
-       \r
-       \r
-       /* is this a decision node? */\r
-       if( node->planenum != PLANENUM_LEAF )\r
-       {\r
-               /* classify the point in relation to the plane */\r
-               plane = &mapplanes[ node->planenum ];\r
-               d = DotProduct( point, plane->normal ) - plane->dist;\r
-               \r
-               /* filter by this plane */\r
-               refs = 0;\r
-               if( d >= -ON_EPSILON )\r
-                       refs += FilterPointIntoTree_r( point, ds, node->children[ 0 ] );\r
-               if( d <= ON_EPSILON )\r
-                       refs += FilterPointIntoTree_r( point, ds, node->children[ 1 ] );\r
-               \r
-               /* return */\r
-               return refs;\r
-       }\r
-       \r
-       /* add a reference */\r
-       return AddReferenceToLeaf( ds, node );\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterWindingIntoTree_r() - ydnar\r
-filters a winding from a drawsurface into the tree\r
-*/\r
-\r
-int FilterWindingIntoTree_r( winding_t *w, mapDrawSurface_t *ds, node_t *node )\r
-{\r
-       int                             i, refs = 0;\r
-       plane_t                 *p1, *p2;\r
-       vec4_t                  plane1, plane2, reverse;\r
-       winding_t               *fat, *front, *back;\r
-       shaderInfo_t    *si;\r
-       \r
-       \r
-       /* get shaderinfo */\r
-       si = ds->shaderInfo;\r
-       \r
-       /* ydnar: is this the head node? */\r
-       if( node->parent == NULL && si != NULL &&\r
-               (si->mins[ 0 ] != 0.0f || si->maxs[ 0 ] != 0.0f ||\r
-               si->mins[ 1 ] != 0.0f || si->maxs[ 1 ] != 0.0f ||\r
-               si->mins[ 2 ] != 0.0f || si->maxs[ 2 ] != 0.0f) )\r
-       {\r
-               /* 'fatten' the winding by the shader mins/maxs (parsed from vertexDeform move) */\r
-               /* note this winding is completely invalid (concave, nonplanar, etc) */\r
-               fat = AllocWinding( w->numpoints * 3 );\r
-               fat->numpoints = w->numpoints * 3;\r
-               for( i = 0; i < w->numpoints; i++ )\r
-               {\r
-                       VectorCopy( w->p[ i ], fat->p[ i ] );\r
-                       VectorAdd( w->p[ i ], si->mins, fat->p[ i * 2 ] );\r
-                       VectorAdd( w->p[ i ], si->maxs, fat->p[ i * 3 ] );\r
-               }\r
-               \r
-               FreeWinding( w );\r
-               w = fat;\r
-       }\r
-       \r
-       /* is this a decision node? */\r
-       if( node->planenum != PLANENUM_LEAF )\r
-       {       \r
-               /* get node plane */\r
-               p1 = &mapplanes[ node->planenum ];\r
-               VectorCopy( p1->normal, plane1 );\r
-               plane1[ 3 ] = p1->dist;\r
-               \r
-               /* check if surface is planar */\r
-               if( ds->planeNum >= 0 )\r
-               {\r
-                       /* get surface plane */\r
-                       p2 = &mapplanes[ ds->planeNum ];\r
-                       VectorCopy( p2->normal, plane2 );\r
-                       plane2[ 3 ] = p2->dist;\r
-                       \r
-                       #if 1\r
-                               /* invert surface plane */\r
-                               VectorSubtract( vec3_origin, plane2, reverse );\r
-                               reverse[ 3 ] = -plane2[ 3 ];\r
-                               \r
-                               /* compare planes */\r
-                               if( DotProduct( plane1, plane2 ) > 0.999f && fabs( plane1[ 3 ] - plane2[ 3 ] ) < 0.001f )\r
-                                       return FilterWindingIntoTree_r( w, ds, node->children[ 0 ] );\r
-                               if( DotProduct( plane1, reverse ) > 0.999f && fabs( plane1[ 3 ] - reverse[ 3 ] ) < 0.001f )\r
-                                       return FilterWindingIntoTree_r( w, ds, node->children[ 1 ] );\r
-                       #else\r
-                               /* the drawsurf might have an associated plane, if so, force a filter here */\r
-                               if( ds->planeNum == node->planenum )\r
-                                       return FilterWindingIntoTree_r( w, ds, node->children[ 0 ] );\r
-                               if( ds->planeNum == (node->planenum ^ 1) )\r
-                                       return FilterWindingIntoTree_r( w, ds, node->children[ 1 ] );\r
-                       #endif\r
-               }\r
-               \r
-               /* clip the winding by this plane */\r
-               ClipWindingEpsilon( w, plane1, plane1[ 3 ], ON_EPSILON, &front, &back );\r
-               \r
-               /* filter by this plane */\r
-               refs = 0;\r
-               if( front != NULL )\r
-                       refs += FilterWindingIntoTree_r( front, ds, node->children[ 0 ] );\r
-               if( back != NULL )\r
-                       refs += FilterWindingIntoTree_r( back, ds, node->children[ 1 ] );\r
-               FreeWinding( w );\r
-               \r
-               /* return */\r
-               return refs;\r
-       }\r
-       \r
-       /* add a reference */\r
-       return AddReferenceToLeaf( ds, node );\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterFaceIntoTree()\r
-filters a planar winding face drawsurface into the bsp tree\r
-*/\r
-\r
-int    FilterFaceIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
-{\r
-       winding_t       *w;\r
-       int                     refs = 0;\r
-       \r
-       \r
-       /* make a winding and filter it into the tree */\r
-       w = WindingFromDrawSurf( ds );\r
-       refs = FilterWindingIntoTree_r( w, ds, tree->headnode );\r
-       \r
-       /* return */\r
-       return refs;\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterPatchIntoTree()\r
-subdivides a patch into an approximate curve and filters it into the tree\r
-*/\r
-\r
-#define        FILTER_SUBDIVISION              8\r
-\r
-static int FilterPatchIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
-{\r
-       int                                     i, x, y, refs;\r
-       mesh_t                          src, *mesh;\r
-       winding_t                       *w;\r
-       \r
-       \r
-       /* subdivide the surface */\r
-       src.width = ds->patchWidth;\r
-       src.height = ds->patchHeight;\r
-       src.verts = ds->verts;\r
-       mesh = SubdivideMesh( src, FILTER_SUBDIVISION, 32 );\r
-       \r
-       \r
-       /* filter each quad into the tree (fixme: use new patch x-triangulation code?) */\r
-       refs = 0;\r
-       for( y = 0; y < (mesh->height - 1); y++ )\r
-       {\r
-               for( x = 0; x < (mesh->width - 1); x++ )\r
-               {\r
-                       /* triangle 1 */\r
-                       w = AllocWinding( 3 );\r
-                       w->numpoints = 3;\r
-                       VectorCopy( mesh->verts[ y * mesh->width + x ].xyz, w->p[ 0 ] );\r
-                       VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 1 ] );\r
-                       VectorCopy( mesh->verts[ (y + 1) * mesh->width + x ].xyz, w->p[ 2 ] );\r
-                       refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
-                       \r
-                       /* triangle 2 */\r
-                       w = AllocWinding( 3 );\r
-                       w->numpoints = 3;\r
-                       VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 0 ] );\r
-                       VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x + 1 ].xyz, w->p[ 1 ] );\r
-                       VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x ].xyz, w->p[ 2 ] );\r
-                       refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
-               }\r
-       }\r
-       \r
-       /* use point filtering as well */\r
-       for( i = 0; i < (mesh->width * mesh->height); i++ )\r
-               refs += FilterPointIntoTree_r( mesh->verts[ i ].xyz, ds, tree->headnode );\r
-       \r
-       /* free the subdivided mesh and return */\r
-       FreeMesh( mesh );\r
-       return refs;\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterTrianglesIntoTree()\r
-filters a triangle surface (meta, model) into the bsp\r
-*/\r
-\r
-static int FilterTrianglesIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
-{\r
-       int                     i, refs;\r
-       winding_t       *w;\r
-       \r
-       \r
-       /* ydnar: gs mods: this was creating bogus triangles before */\r
-       refs = 0;\r
-       for( i = 0; i < ds->numIndexes; i += 3 )\r
-       {\r
-               /* error check */\r
-               if( ds->indexes[ i ] >= ds->numVerts ||\r
-                       ds->indexes[ i + 1 ] >= ds->numVerts ||\r
-                       ds->indexes[ i + 2 ] >= ds->numVerts )\r
-                       Error( "Index %d greater than vertex count %d", ds->indexes[ i ], ds->numVerts );\r
-               \r
-               /* make a triangle winding and filter it into the tree */\r
-               w = AllocWinding( 3 );\r
-               w->numpoints = 3;\r
-               VectorCopy( ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );\r
-               VectorCopy( ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );\r
-               VectorCopy( ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );\r
-               refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
-       }\r
-       \r
-       /* use point filtering as well */\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-               refs += FilterPointIntoTree_r( ds->verts[ i ].xyz, ds, tree->headnode );\r
-\r
-       return refs;\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterFoliageIntoTree()\r
-filters a foliage surface (wolf et/splash damage)\r
-*/\r
-\r
-static int FilterFoliageIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
-{\r
-       int                             f, i, refs;\r
-       bspDrawVert_t   *instance;\r
-       vec3_t                  xyz;\r
-       winding_t               *w;\r
-       \r
-       \r
-       /* walk origin list */\r
-       refs = 0;\r
-       for( f = 0; f < ds->numFoliageInstances; f++ )\r
-       {\r
-               /* get instance */\r
-               instance = ds->verts + ds->patchHeight + f;\r
-               \r
-               /* walk triangle list */\r
-               for( i = 0; i < ds->numIndexes; i += 3 )\r
-               {\r
-                       /* error check */\r
-                       if( ds->indexes[ i ] >= ds->numVerts ||\r
-                               ds->indexes[ i + 1 ] >= ds->numVerts ||\r
-                               ds->indexes[ i + 2 ] >= ds->numVerts )\r
-                               Error( "Index %d greater than vertex count %d", ds->indexes[ i ], ds->numVerts );\r
-                       \r
-                       /* make a triangle winding and filter it into the tree */\r
-                       w = AllocWinding( 3 );\r
-                       w->numpoints = 3;\r
-                       VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );\r
-                       VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );\r
-                       VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );\r
-                       refs += FilterWindingIntoTree_r( w, ds, tree->headnode );\r
-               }\r
-               \r
-               /* use point filtering as well */\r
-               for( i = 0; i < (ds->numVerts - ds->numFoliageInstances); i++ )\r
-               {\r
-                       VectorAdd( instance->xyz, ds->verts[ i ].xyz, xyz );\r
-                       refs += FilterPointIntoTree_r( xyz, ds, tree->headnode );\r
-               }\r
-       }\r
-       \r
-       return refs;\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterFlareIntoTree()\r
-simple point filtering for flare surfaces\r
-*/\r
-static int FilterFlareSurfIntoTree( mapDrawSurface_t *ds, tree_t *tree )\r
-{\r
-       return FilterPointIntoTree_r( ds->lightmapOrigin, ds, tree->headnode );\r
-}\r
-\r
-\r
-\r
-/*\r
-EmitDrawVerts() - ydnar\r
-emits bsp drawverts from a map drawsurface\r
-*/\r
-\r
-void EmitDrawVerts( mapDrawSurface_t *ds, bspDrawSurface_t *out )\r
-{\r
-       int                             i, k;\r
-       bspDrawVert_t   *dv;\r
-       shaderInfo_t    *si;\r
-       float                   offset;\r
-       \r
-       \r
-       /* get stuff */\r
-       si = ds->shaderInfo;\r
-       offset = si->offset;\r
-       \r
-       /* copy the verts */\r
-       out->firstVert = numBSPDrawVerts;\r
-       out->numVerts = ds->numVerts;\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-       {\r
-               /* allocate a new vert */\r
-               if( numBSPDrawVerts == MAX_MAP_DRAW_VERTS )\r
-                       Error( "MAX_MAP_DRAW_VERTS" );\r
-               IncDrawVerts();\r
-               dv = &bspDrawVerts[ numBSPDrawVerts - 1 ];\r
-               \r
-               /* copy it */\r
-               memcpy( dv, &ds->verts[ i ], sizeof( *dv ) );\r
-               \r
-               /* offset? */\r
-               if( offset != 0.0f )\r
-                       VectorMA( dv->xyz, offset, dv->normal, dv->xyz );\r
-               \r
-               /* expand model bounds\r
-                  necessary because of misc_model surfaces on entities\r
-                  note: does not happen on worldspawn as its bounds is only used for determining lightgrid bounds */\r
-               if( numBSPModels > 0 )\r
-                       AddPointToBounds( dv->xyz, bspModels[ numBSPModels ].mins, bspModels[ numBSPModels ].maxs );\r
-               \r
-               /* debug color? */\r
-               if( debugSurfaces )\r
-               {\r
-                       for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                               VectorCopy( debugColors[ (ds - mapDrawSurfs) % 12 ], dv->color[ k ] );\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-FindDrawIndexes() - ydnar\r
-this attempts to find a run of indexes in the bsp that match the given indexes\r
-this tends to reduce the size of the bsp index pool by 1/3 or more\r
-returns numIndexes + 1 if the search failed\r
-*/\r
-\r
-int FindDrawIndexes( int numIndexes, int *indexes )\r
-{\r
-       int             i, j, numTestIndexes;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( numIndexes < 3 || numBSPDrawIndexes < numIndexes || indexes == NULL )\r
-               return numBSPDrawIndexes;\r
-       \r
-       /* set limit */\r
-       numTestIndexes = 1 + numBSPDrawIndexes - numIndexes;\r
-       \r
-       /* handle 3 indexes as a special case for performance */\r
-       if( numIndexes == 3 )\r
-       {\r
-               /* run through all indexes */\r
-               for( i = 0; i < numTestIndexes; i++ )\r
-               {\r
-                       /* test 3 indexes */\r
-                       if( indexes[ 0 ] == bspDrawIndexes[ i ] &&\r
-                               indexes[ 1 ] == bspDrawIndexes[ i + 1 ] &&\r
-                               indexes[ 2 ] == bspDrawIndexes[ i + 2 ] )\r
-                       {\r
-                               numRedundantIndexes += numIndexes;\r
-                               return i;\r
-                       }\r
-               }\r
-               \r
-               /* failed */\r
-               return numBSPDrawIndexes;\r
-       }\r
-       \r
-       /* handle 4 or more indexes */\r
-       for( i = 0; i < numTestIndexes; i++ )\r
-       {\r
-               /* test first 4 indexes */\r
-               if( indexes[ 0 ] == bspDrawIndexes[ i ] &&\r
-                       indexes[ 1 ] == bspDrawIndexes[ i + 1 ] &&\r
-                       indexes[ 2 ] == bspDrawIndexes[ i + 2 ] &&\r
-                       indexes[ 3 ] == bspDrawIndexes[ i + 3 ] )\r
-               {\r
-                       /* handle 4 indexes */\r
-                       if( numIndexes == 4 )\r
-                               return i;\r
-                       \r
-                       /* test the remainder */\r
-                       for( j = 4; j < numIndexes; j++ )\r
-                       {\r
-                               if( indexes[ j ] != bspDrawIndexes[ i + j ] )\r
-                                       break;\r
-                               else if( j == (numIndexes - 1) )\r
-                               {\r
-                                       numRedundantIndexes += numIndexes;\r
-                                       return i;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* failed */\r
-       return numBSPDrawIndexes;\r
-}\r
-\r
-\r
-\r
-/*\r
-EmitDrawIndexes() - ydnar\r
-attempts to find an existing run of drawindexes before adding new ones\r
-*/\r
-\r
-void EmitDrawIndexes( mapDrawSurface_t *ds, bspDrawSurface_t *out )\r
-{\r
-       int                     i;\r
-       \r
-       \r
-       /* attempt to use redundant indexing */\r
-       out->firstIndex = FindDrawIndexes( ds->numIndexes, ds->indexes );\r
-       out->numIndexes = ds->numIndexes;\r
-       if( out->firstIndex == numBSPDrawIndexes )\r
-       {\r
-               /* copy new unique indexes */\r
-               for( i = 0; i < ds->numIndexes; i++ )\r
-               {\r
-                       if( numBSPDrawIndexes == MAX_MAP_DRAW_INDEXES )\r
-                               Error( "MAX_MAP_DRAW_INDEXES" );\r
-                       bspDrawIndexes[ numBSPDrawIndexes ] = ds->indexes[ i ];\r
-\r
-                       /* validate the index */\r
-                       if( ds->type != SURFACE_PATCH )\r
-                       {\r
-                               if( bspDrawIndexes[ numBSPDrawIndexes ] < 0 || bspDrawIndexes[ numBSPDrawIndexes ] >= ds->numVerts )\r
-                               {\r
-                                       Sys_Printf( "WARNING: %d %s has invalid index %d (%d)\n",\r
-                                               numBSPDrawSurfaces,\r
-                                               ds->shaderInfo->shader,\r
-                                               bspDrawIndexes[ numBSPDrawIndexes ],\r
-                                               i );\r
-                                       bspDrawIndexes[ numBSPDrawIndexes ] = 0;\r
-                               }\r
-                       }\r
-                       \r
-                       /* increment index count */\r
-                       numBSPDrawIndexes++;\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-EmitFlareSurface()\r
-emits a bsp flare drawsurface\r
-*/\r
-\r
-void EmitFlareSurface( mapDrawSurface_t *ds )\r
-{\r
-       int                                             i;\r
-       bspDrawSurface_t                *out;\r
-       \r
-       \r
-       /* ydnar: nuking useless flare drawsurfaces */\r
-       if( emitFlares == qfalse && ds->type != SURFACE_SHADER )\r
-               return;\r
-       \r
-       /* limit check */\r
-       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
-               Error( "MAX_MAP_DRAW_SURFS" );\r
-       \r
-       /* allocate a new surface */\r
-       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
-               Error( "MAX_MAP_DRAW_SURFS" );\r
-       out = &bspDrawSurfaces[ numBSPDrawSurfaces ];\r
-       ds->outputNum = numBSPDrawSurfaces;\r
-       numBSPDrawSurfaces++;\r
-       memset( out, 0, sizeof( *out ) );\r
-       \r
-       /* set it up */\r
-       out->surfaceType = MST_FLARE;\r
-       out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );\r
-       out->fogNum = ds->fogNum;\r
-       \r
-       /* RBSP */\r
-       for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
-       {\r
-               out->lightmapNum[ i ] = -3;\r
-               out->lightmapStyles[ i ] = LS_NONE;\r
-               out->vertexStyles[ i ] = LS_NONE;\r
-       }\r
-       out->lightmapStyles[ 0 ] = ds->lightStyle;\r
-       out->vertexStyles[ 0 ] = ds->lightStyle;\r
-       \r
-       VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );                  /* origin */\r
-       VectorCopy( ds->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );    /* color */\r
-       VectorCopy( ds->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );\r
-       VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );    /* normal */\r
-       \r
-       /* add to count */\r
-       numSurfacesByType[ ds->type ]++;\r
-}\r
-\r
-\r
-\r
-/*\r
-EmitPatchSurface()\r
-emits a bsp patch drawsurface\r
-*/\r
-\r
-void EmitPatchSurface( mapDrawSurface_t *ds )\r
-{\r
-       int                                     i, j;\r
-       bspDrawSurface_t        *out;\r
-       int                                     surfaceFlags, contentFlags;\r
-       \r
-       \r
-       /* invert the surface if necessary */\r
-       if( ds->shaderInfo->invert )\r
-       {\r
-               bspDrawVert_t   *dv1, *dv2, temp;\r
-               \r
-\r
-               /* walk the verts, flip the normal */\r
-               for( i = 0; i < ds->numVerts; i++ )\r
-                       VectorScale( ds->verts[ i ].normal, -1.0f, ds->verts[ i ].normal );\r
-               \r
-               /* walk the verts again, but this time reverse their order */\r
-               for( j = 0; j < ds->patchHeight; j++ )\r
-               {\r
-                       for( i = 0; i < (ds->patchWidth / 2); i++ )\r
-                       {\r
-                               dv1 = &ds->verts[ j * ds->patchWidth + i ];\r
-                               dv2 = &ds->verts[ j * ds->patchWidth + (ds->patchWidth - i - 1) ];\r
-                               memcpy( &temp, dv1, sizeof( bspDrawVert_t ) );\r
-                               memcpy( dv1, dv2, sizeof( bspDrawVert_t ) );\r
-                               memcpy( dv2, &temp, sizeof( bspDrawVert_t ) );\r
-                       }\r
-               }\r
-               \r
-               /* invert facing */\r
-               VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );\r
-       }\r
-       \r
-       /* allocate a new surface */\r
-       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
-               Error( "MAX_MAP_DRAW_SURFS" );\r
-       out = &bspDrawSurfaces[ numBSPDrawSurfaces ];\r
-       ds->outputNum = numBSPDrawSurfaces;\r
-       numBSPDrawSurfaces++;\r
-       memset( out, 0, sizeof( *out ) );\r
-       \r
-       /* set it up */\r
-       out->surfaceType = MST_PATCH;\r
-       if( debugSurfaces )\r
-               out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );\r
-       else if( patchMeta )\r
-       {\r
-               /* patch meta requires that we have nodraw patches for collision */\r
-               surfaceFlags = ds->shaderInfo->surfaceFlags;\r
-               contentFlags = ds->shaderInfo->contentFlags;\r
-               ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, NULL );\r
-               ApplySurfaceParm( "pointlight", &contentFlags, &surfaceFlags, NULL );\r
-               \r
-               /* we don't want this patch getting lightmapped */\r
-               VectorClear( ds->lightmapVecs[ 2 ] );\r
-               VectorClear( ds->lightmapAxis );\r
-               ds->sampleSize = 0;\r
-\r
-               /* emit the new fake shader */\r
-               out->shaderNum = EmitShader( ds->shaderInfo->shader, &contentFlags, &surfaceFlags );\r
-       }\r
-       else\r
-               out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );\r
-       out->patchWidth = ds->patchWidth;\r
-       out->patchHeight = ds->patchHeight;\r
-       out->fogNum = ds->fogNum;\r
-       \r
-       /* RBSP */\r
-       for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
-       {\r
-               out->lightmapNum[ i ] = -3;\r
-               out->lightmapStyles[ i ] = LS_NONE;\r
-               out->vertexStyles[ i ] = LS_NONE;\r
-       }\r
-       out->lightmapStyles[ 0 ] = LS_NORMAL;\r
-       out->vertexStyles[ 0 ] = LS_NORMAL;\r
-       \r
-       /* ydnar: gs mods: previously, the lod bounds were stored in lightmapVecs[ 0 ] and [ 1 ], moved to bounds[ 0 ] and [ 1 ] */\r
-       VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );\r
-       VectorCopy( ds->bounds[ 0 ], out->lightmapVecs[ 0 ] );\r
-       VectorCopy( ds->bounds[ 1 ], out->lightmapVecs[ 1 ] );\r
-       VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );\r
-       \r
-       /* ydnar: gs mods: clear out the plane normal */\r
-       if( ds->planar == qfalse )\r
-               VectorClear( out->lightmapVecs[ 2 ] );\r
-       \r
-       /* emit the verts and indexes */\r
-       EmitDrawVerts( ds, out );\r
-       EmitDrawIndexes( ds, out );\r
-       \r
-       /* add to count */\r
-       numSurfacesByType[ ds->type ]++;\r
-}\r
-\r
-\r
-\r
-/*\r
-OptimizeTriangleSurface() - ydnar\r
-optimizes the vertex/index data in a triangle surface\r
-*/\r
-\r
-#define VERTEX_CACHE_SIZE      16\r
-\r
-static void OptimizeTriangleSurface( mapDrawSurface_t *ds )\r
-{\r
-       int             i, j, k, temp, first, best, bestScore, score;\r
-       int             vertexCache[ VERTEX_CACHE_SIZE + 1 ];   /* one more for optimizing insert */\r
-       int             *indexes;\r
-       \r
-       \r
-       /* certain surfaces don't get optimized */\r
-       if( ds->numIndexes <= VERTEX_CACHE_SIZE ||\r
-               ds->shaderInfo->autosprite )\r
-               return;\r
-       \r
-       /* create index scratch pad */\r
-       indexes = safe_malloc( ds->numIndexes * sizeof( *indexes ) );\r
-       memcpy( indexes, ds->indexes, ds->numIndexes * sizeof( *indexes ) );\r
-       \r
-       /* setup */\r
-       for( i = 0; i <= VERTEX_CACHE_SIZE && i < ds->numIndexes; i++ )\r
-               vertexCache[ i ] = indexes[ i ];\r
-       \r
-       /* add triangles in a vertex cache-aware order */\r
-       for( i = 0; i < ds->numIndexes; i += 3 )\r
-       {\r
-               /* find best triangle given the current vertex cache */\r
-               first = -1;\r
-               best = -1;\r
-               bestScore = -1;\r
-               for( j = 0; j < ds->numIndexes; j += 3 )\r
-               {\r
-                       /* valid triangle? */\r
-                       if( indexes[ j ] != -1 )\r
-                       {\r
-                               /* set first if necessary */\r
-                               if( first < 0 )\r
-                                       first = j;\r
-                               \r
-                               /* score the triangle */\r
-                               score = 0;\r
-                               for( k = 0; k < VERTEX_CACHE_SIZE; k++ )\r
-                               {\r
-                                       if( indexes[ j ] == vertexCache[ k ] || indexes[ j + 1 ] == vertexCache[ k ] || indexes[ j + 2 ] == vertexCache[ k ] )\r
-                                               score++;\r
-                               }\r
-                               \r
-                               /* better triangle? */\r
-                               if( score > bestScore )\r
-                               {\r
-                                       bestScore = score;\r
-                                       best = j;\r
-                               }\r
-                               \r
-                               /* a perfect score of 3 means this triangle's verts are already present in the vertex cache */\r
-                               if( score == 3 )\r
-                                       break;\r
-                       }\r
-               }\r
-               \r
-               /* check if no decent triangle was found, and use first available */\r
-               if( best < 0 )\r
-                       best = first;\r
-               \r
-               /* valid triangle? */\r
-               if( best >= 0 )\r
-               {\r
-                       /* add triangle to vertex cache */\r
-                       for( j = 0; j < 3; j++ )\r
-                       {\r
-                               for( k = 0; k < VERTEX_CACHE_SIZE; k++ )\r
-                               {\r
-                                       if( indexes[ best + j ] == vertexCache[ k ] )\r
-                                               break;\r
-                               }\r
-                               \r
-                               if( k >= VERTEX_CACHE_SIZE )\r
-                               {\r
-                                       /* pop off top of vertex cache */\r
-                                       for( k = VERTEX_CACHE_SIZE; k > 0; k-- )\r
-                                               vertexCache[ k ] = vertexCache[ k - 1 ];\r
-                                       \r
-                                       /* add vertex */\r
-                                       vertexCache[ 0 ] = indexes[ best + j ];\r
-                               }\r
-                       }\r
-                       \r
-                       /* add triangle to surface */\r
-                       ds->indexes[ i ] = indexes[ best ];\r
-                       ds->indexes[ i + 1 ] = indexes[ best + 1 ];\r
-                       ds->indexes[ i + 2 ] = indexes[ best + 2 ];\r
-                       \r
-                       /* clear from input pool */\r
-                       indexes[ best ] = -1;\r
-                       indexes[ best + 1 ] = -1;\r
-                       indexes[ best + 2 ] = -1;\r
-                       \r
-                       /* sort triangle windings (312 -> 123) */\r
-                       while( ds->indexes[ i ] > ds->indexes[ i + 1 ] || ds->indexes[ i ] > ds->indexes[ i + 2 ] )\r
-                       {\r
-                               temp = ds->indexes[ i ];\r
-                               ds->indexes[ i ] = ds->indexes[ i + 1 ];\r
-                               ds->indexes[ i + 1 ] = ds->indexes[ i + 2 ];\r
-                               ds->indexes[ i + 2 ] = temp;\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* clean up */\r
-       free( indexes );\r
-}\r
-\r
-\r
-\r
-/*\r
-EmitTriangleSurface()\r
-creates a bsp drawsurface from arbitrary triangle surfaces\r
-*/\r
-\r
-static void EmitTriangleSurface( mapDrawSurface_t *ds )\r
-{\r
-       int                                             i, temp;\r
-       bspDrawSurface_t                *out;\r
-       \r
-       \r
-       /* invert the surface if necessary */\r
-       if( ds->shaderInfo->invert )\r
-       {\r
-               /* walk the indexes, reverse the triangle order */\r
-               for( i = 0; i < ds->numIndexes; i += 3 )\r
-               {\r
-                       temp = ds->indexes[ i ];\r
-                       ds->indexes[ i ] = ds->indexes[ i + 1 ];\r
-                       ds->indexes[ i + 1 ] = temp;\r
-               }\r
-               \r
-               /* walk the verts, flip the normal */\r
-               for( i = 0; i < ds->numVerts; i++ )\r
-                       VectorScale( ds->verts[ i ].normal, -1.0f, ds->verts[ i ].normal );\r
-               \r
-               /* invert facing */\r
-               VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );\r
-       }\r
-       \r
-       /* allocate a new surface */\r
-       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )\r
-               Error( "MAX_MAP_DRAW_SURFS" );\r
-       out = &bspDrawSurfaces[ numBSPDrawSurfaces ];\r
-       ds->outputNum = numBSPDrawSurfaces;\r
-       numBSPDrawSurfaces++;\r
-       memset( out, 0, sizeof( *out ) );\r
-       \r
-       /* ydnar/sd: handle wolf et foliage surfaces */\r
-       if( ds->type == SURFACE_FOLIAGE )\r
-               out->surfaceType = MST_FOLIAGE;\r
-       \r
-       /* ydnar: gs mods: handle lightmapped terrain (force to planar type) */\r
-       //%     else if( VectorLength( ds->lightmapAxis ) <= 0.0f || ds->type == SURFACE_TRIANGLES || ds->type == SURFACE_FOGHULL || debugSurfaces )\r
-       else if( (VectorLength( ds->lightmapAxis ) <= 0.0f && ds->planar == qfalse) || ds->type == SURFACE_TRIANGLES || ds->type == SURFACE_FOGHULL || debugSurfaces )\r
-               out->surfaceType = MST_TRIANGLE_SOUP;\r
-       \r
-       /* set to a planar face */\r
-       else\r
-               out->surfaceType = MST_PLANAR;\r
-       \r
-       /* set it up */\r
-       if( debugSurfaces )\r
-               out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );\r
-       else\r
-               out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );\r
-       out->patchWidth = ds->patchWidth;\r
-       out->patchHeight = ds->patchHeight;\r
-       out->fogNum = ds->fogNum;\r
-       \r
-       /* debug inset (push each triangle vertex towards the center of each triangle it is on */\r
-       if( debugInset )\r
-       {\r
-               bspDrawVert_t   *a, *b, *c;\r
-               vec3_t                  cent, dir;\r
-\r
-               \r
-               /* walk triangle list */\r
-               for( i = 0; i < ds->numIndexes; i += 3 )\r
-               {\r
-                       /* get verts */\r
-                       a = &ds->verts[ ds->indexes[ i ] ];\r
-                       b = &ds->verts[ ds->indexes[ i + 1 ] ];\r
-                       c = &ds->verts[ ds->indexes[ i + 2 ] ];\r
-                       \r
-                       /* calculate centroid */\r
-                       VectorCopy( a->xyz, cent );\r
-                       VectorAdd( cent, b->xyz, cent );\r
-                       VectorAdd( cent, c->xyz, cent );\r
-                       VectorScale( cent, 1.0f / 3.0f, cent );\r
-                       \r
-                       /* offset each vertex */\r
-                       VectorSubtract( cent, a->xyz, dir );\r
-                       VectorNormalize( dir, dir );\r
-                       VectorAdd( a->xyz, dir, a->xyz );\r
-                       VectorSubtract( cent, b->xyz, dir );\r
-                       VectorNormalize( dir, dir );\r
-                       VectorAdd( b->xyz, dir, b->xyz );\r
-                       VectorSubtract( cent, c->xyz, dir );\r
-                       VectorNormalize( dir, dir );\r
-                       VectorAdd( c->xyz, dir, c->xyz );\r
-               }\r
-       }\r
-       \r
-       /* RBSP */\r
-       for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
-       {\r
-               out->lightmapNum[ i ] = -3;\r
-               out->lightmapStyles[ i ] = LS_NONE;\r
-               out->vertexStyles[ i ] = LS_NONE;\r
-       }\r
-       out->lightmapStyles[ 0 ] = LS_NORMAL;\r
-       out->vertexStyles[ 0 ] = LS_NORMAL;\r
-       \r
-       /* lightmap vectors (lod bounds for patches */\r
-       VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );\r
-       VectorCopy( ds->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );\r
-       VectorCopy( ds->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );\r
-       VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );\r
-       \r
-       /* ydnar: gs mods: clear out the plane normal */\r
-       if( ds->planar == qfalse )\r
-               VectorClear( out->lightmapVecs[ 2 ] );\r
-       \r
-       /* optimize the surface's triangles */\r
-       OptimizeTriangleSurface( ds );\r
-       \r
-       /* emit the verts and indexes */\r
-       EmitDrawVerts( ds, out );\r
-       EmitDrawIndexes( ds, out );\r
-       \r
-       /* add to count */\r
-       numSurfacesByType[ ds->type ]++;\r
-}\r
-\r
-\r
-\r
-/*\r
-EmitFaceSurface()\r
-emits a bsp planar winding (brush face) drawsurface\r
-*/\r
-\r
-static void EmitFaceSurface( mapDrawSurface_t *ds )\r
-{\r
-       /* strip/fan finding was moved elsewhere */\r
-       StripFaceSurface( ds );\r
-       EmitTriangleSurface( ds );\r
-}\r
-\r
-\r
-\r
-/*\r
-MakeDebugPortalSurfs_r() - ydnar\r
-generates drawsurfaces for passable portals in the bsp\r
-*/\r
-\r
-static void MakeDebugPortalSurfs_r( node_t *node, shaderInfo_t *si )\r
-{\r
-       int                                     i, k, c, s;     \r
-       portal_t                        *p;\r
-       winding_t                       *w;\r
-       mapDrawSurface_t        *ds;\r
-       bspDrawVert_t           *dv;\r
-       \r
-       \r
-       /* recurse if decision node */\r
-       if( node->planenum != PLANENUM_LEAF)\r
-       {\r
-               MakeDebugPortalSurfs_r( node->children[ 0 ], si );\r
-               MakeDebugPortalSurfs_r( node->children[ 1 ], si );\r
-               return;\r
-       }\r
-       \r
-       /* don't bother with opaque leaves */\r
-       if( node->opaque )\r
-               return;\r
-       \r
-       /* walk the list of portals */\r
-       for( c = 0, p = node->portals; p != NULL; c++, p = p->next[ s ] )\r
-       {\r
-               /* get winding and side even/odd */\r
-               w = p->winding;\r
-               s = (p->nodes[ 1 ] == node);\r
-               \r
-               /* is this a valid portal for this leaf? */\r
-               if( w && p->nodes[ 0 ] == node )\r
-               {\r
-                       /* is this portal passable? */\r
-                       if( PortalPassable( p ) == qfalse )\r
-                               continue;\r
-                       \r
-                       /* check max points */\r
-                       if( w->numpoints > 64 )\r
-                               Error( "MakePortalSurfs_r: w->numpoints = %d", w->numpoints );\r
-                       \r
-                       /* allocate a drawsurface */\r
-                       ds = AllocDrawSurface( SURFACE_FACE );\r
-                       ds->shaderInfo = si;\r
-                       ds->planar = qtrue;\r
-                       ds->sideRef = AllocSideRef( p->side, NULL );\r
-                       ds->planeNum = FindFloatPlane( p->plane.normal, p->plane.dist, 0, NULL );\r
-                       VectorCopy( p->plane.normal, ds->lightmapVecs[ 2 ] );\r
-                       ds->fogNum = -1;\r
-                       ds->numVerts = w->numpoints;\r
-                       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
-                       memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );\r
-                       \r
-                       /* walk the winding */\r
-                       for( i = 0; i < ds->numVerts; i++ )\r
-                       {\r
-                               /* get vert */\r
-                               dv = ds->verts + i;\r
-                               \r
-                               /* set it */\r
-                               VectorCopy( w->p[ i ], dv->xyz );\r
-                               VectorCopy( p->plane.normal, dv->normal );\r
-                               dv->st[ 0 ] = 0;\r
-                               dv->st[ 1 ] = 0;\r
-                               for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
-                               {\r
-                                       VectorCopy( debugColors[ c % 12 ], dv->color[ k ] );\r
-                                       dv->color[ k ][ 3 ] = 32;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-MakeDebugPortalSurfs() - ydnar\r
-generates drawsurfaces for passable portals in the bsp\r
-*/\r
-\r
-void MakeDebugPortalSurfs( tree_t *tree )\r
-{\r
-       shaderInfo_t    *si;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- MakeDebugPortalSurfs ---\n" );\r
-       \r
-       /* get portal debug shader */\r
-       si = ShaderInfoForShader( "debugportals" );\r
-       \r
-       /* walk the tree */\r
-       MakeDebugPortalSurfs_r( tree->headnode, si );\r
-}\r
-\r
-\r
-\r
-/*\r
-MakeFogHullSurfs()\r
-generates drawsurfaces for a foghull (this MUST use a sky shader)\r
-*/\r
-\r
-void MakeFogHullSurfs( entity_t *e, tree_t *tree, char *shader )\r
-{\r
-       shaderInfo_t            *si;\r
-       mapDrawSurface_t        *ds;\r
-       vec3_t                          fogMins, fogMaxs;\r
-       int                                     i, indexes[] =\r
-                                               {\r
-                                                       0, 1, 2, 0, 2, 3,\r
-                                                       4, 7, 5, 5, 7, 6,\r
-                                                       1, 5, 6, 1, 6, 2,\r
-                                                       0, 4, 5, 0, 5, 1,\r
-                                                       2, 6, 7, 2, 7, 3,\r
-                                                       3, 7, 4, 3, 4, 0\r
-                                               };\r
-\r
-       \r
-       /* dummy check */\r
-       if( shader == NULL || shader[ 0 ] == '\0' )\r
-               return;\r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- MakeFogHullSurfs ---\n" );\r
-       \r
-       /* get hull bounds */\r
-       VectorCopy( mapMins, fogMins );\r
-       VectorCopy( mapMaxs, fogMaxs );\r
-       for( i = 0; i < 3; i++ )\r
-       {\r
-               fogMins[ i ] -= 128;\r
-               fogMaxs[ i ] += 128;\r
-       }\r
-       \r
-       /* get foghull shader */\r
-       si = ShaderInfoForShader( shader );\r
-       \r
-       /* allocate a drawsurface */\r
-       ds = AllocDrawSurface( SURFACE_FOGHULL );\r
-       ds->shaderInfo = si;\r
-       ds->fogNum = -1;\r
-       ds->numVerts = 8;\r
-       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );\r
-       memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );\r
-       ds->numIndexes = 36;\r
-       ds->indexes = safe_malloc( ds->numIndexes * sizeof( *ds->indexes ) );\r
-       memset( ds->indexes, 0, ds->numIndexes * sizeof( *ds->indexes ) );\r
-       \r
-       /* set verts */\r
-       VectorSet( ds->verts[ 0 ].xyz, fogMins[ 0 ], fogMins[ 1 ], fogMins[ 2 ] );\r
-       VectorSet( ds->verts[ 1 ].xyz, fogMins[ 0 ], fogMaxs[ 1 ], fogMins[ 2 ] );\r
-       VectorSet( ds->verts[ 2 ].xyz, fogMaxs[ 0 ], fogMaxs[ 1 ], fogMins[ 2 ] );\r
-       VectorSet( ds->verts[ 3 ].xyz, fogMaxs[ 0 ], fogMins[ 1 ], fogMins[ 2 ] );\r
-       \r
-       VectorSet( ds->verts[ 4 ].xyz, fogMins[ 0 ], fogMins[ 1 ], fogMaxs[ 2 ] );\r
-       VectorSet( ds->verts[ 5 ].xyz, fogMins[ 0 ], fogMaxs[ 1 ], fogMaxs[ 2 ] );\r
-       VectorSet( ds->verts[ 6 ].xyz, fogMaxs[ 0 ], fogMaxs[ 1 ], fogMaxs[ 2 ] );\r
-       VectorSet( ds->verts[ 7 ].xyz, fogMaxs[ 0 ], fogMins[ 1 ], fogMaxs[ 2 ] );\r
-       \r
-       /* set indexes */\r
-       memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( *ds->indexes ) );\r
-}\r
-\r
-\r
-\r
-/*\r
-BiasSurfaceTextures()\r
-biases a surface's texcoords as close to 0 as possible\r
-*/\r
-\r
-void BiasSurfaceTextures( mapDrawSurface_t *ds )\r
-{\r
-       int             i;\r
-       \r
-       \r
-       /* calculate the surface texture bias */\r
-       CalcSurfaceTextureRange( ds );\r
-       \r
-       /* don't bias globaltextured shaders */\r
-       if( ds->shaderInfo->globalTexture )\r
-               return;\r
-       \r
-       /* bias the texture coordinates */\r
-       for( i = 0; i < ds->numVerts; i++ )\r
-       {\r
-               ds->verts[ i ].st[ 0 ] += ds->bias[ 0 ];\r
-               ds->verts[ i ].st[ 1 ] += ds->bias[ 1 ];\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-AddSurfaceModelsToTriangle_r()\r
-adds models to a specified triangle, returns the number of models added\r
-*/\r
-\r
-int AddSurfaceModelsToTriangle_r( mapDrawSurface_t *ds, surfaceModel_t *model, bspDrawVert_t **tri )\r
-{\r
-       bspDrawVert_t   mid, *tri2[ 3 ];\r
-       int                             max, n, localNumSurfaceModels;\r
-       \r
-       \r
-       /* init */\r
-       localNumSurfaceModels = 0;\r
-       \r
-       /* subdivide calc */\r
-       {\r
-               int                     i;\r
-               float           *a, *b, dx, dy, dz, dist, maxDist;\r
-               \r
-               \r
-               /* find the longest edge and split it */\r
-               max = -1;\r
-               maxDist = 0.0f;\r
-               for( i = 0; i < 3; i++ )\r
-               {\r
-                       /* get verts */\r
-                       a = tri[ i ]->xyz;\r
-                       b = tri[ (i + 1) % 3 ]->xyz;\r
-                       \r
-                       /* get dists */\r
-                       dx = a[ 0 ] - b[ 0 ];\r
-                       dy = a[ 1 ] - b[ 1 ];\r
-                       dz = a[ 2 ] - b[ 2 ];\r
-                       dist = (dx * dx) + (dy * dy) + (dz * dz);\r
-                       \r
-                       /* longer? */\r
-                       if( dist > maxDist )\r
-                       {\r
-                               maxDist = dist;\r
-                               max = i;\r
-                       }\r
-               }\r
-               \r
-               /* is the triangle small enough? */\r
-               if( max < 0 || maxDist <= (model->density * model->density) )\r
-               {\r
-                       float   odds, r, angle;\r
-                       vec3_t  origin, normal, scale, axis[ 3 ], angles;\r
-                       m4x4_t  transform, temp;\r
-\r
-                       \r
-                       /* roll the dice (model's odds scaled by vertex alpha) */\r
-                       odds = model->odds * (tri[ 0 ]->color[ 0 ][ 3 ] + tri[ 0 ]->color[ 0 ][ 3 ] + tri[ 0 ]->color[ 0 ][ 3 ]) / 765.0f;\r
-                       r = Random();\r
-                       if( r > model->odds )\r
-                               return 0;\r
-                       \r
-                       /* calculate scale */\r
-                       r = model->minScale + Random() * (model->maxScale - model->minScale);\r
-                       VectorSet( scale, r, r, r );\r
-                       \r
-                       /* calculate angle */\r
-                       angle = model->minAngle + Random() * (model->maxAngle - model->minAngle);\r
-                       \r
-                       /* calculate average origin */\r
-                       VectorCopy( tri[ 0 ]->xyz, origin );\r
-                       VectorAdd( origin, tri[ 1 ]->xyz, origin );\r
-                       VectorAdd( origin, tri[ 2 ]->xyz, origin );\r
-                       VectorScale( origin, (1.0f / 3.0f), origin );\r
-                       \r
-                       /* clear transform matrix */\r
-                       m4x4_identity( transform );\r
-\r
-                       /* handle oriented models */\r
-                       if( model->oriented )\r
-                       {\r
-                               /* set angles */\r
-                               VectorSet( angles, 0.0f, 0.0f, angle );\r
-                               \r
-                               /* calculate average normal */\r
-                               VectorCopy( tri[ 0 ]->normal, normal );\r
-                               VectorAdd( normal, tri[ 1 ]->normal, normal );\r
-                               VectorAdd( normal, tri[ 2 ]->normal, normal );\r
-                               if( VectorNormalize( normal, axis[ 2 ] ) == 0.0f )\r
-                                       VectorCopy( tri[ 0 ]->normal, axis[ 2 ] );\r
-                               \r
-                               /* make perpendicular vectors */\r
-                               MakeNormalVectors( axis[ 2 ], axis[ 1 ], axis[ 0 ] );\r
-                               \r
-                               /* copy to matrix */\r
-                               m4x4_identity( temp );\r
-                               temp[ 0 ] = axis[ 0 ][ 0 ];     temp[ 1 ] = axis[ 0 ][ 1 ];     temp[ 2 ] = axis[ 0 ][ 2 ];\r
-                               temp[ 4 ] = axis[ 1 ][ 0 ];     temp[ 5 ] = axis[ 1 ][ 1 ];     temp[ 6 ] = axis[ 1 ][ 2 ];\r
-                               temp[ 8 ] = axis[ 2 ][ 0 ];     temp[ 9 ] = axis[ 2 ][ 1 ];     temp[ 10 ] = axis[ 2 ][ 2 ];\r
-                               \r
-                               /* scale */\r
-                               m4x4_scale_by_vec3( temp, scale );\r
-                               \r
-                               /* rotate around z axis */\r
-                               m4x4_rotate_by_vec3( temp, angles, eXYZ );\r
-                               \r
-                               /* translate */\r
-                               m4x4_translate_by_vec3( transform, origin );\r
-                               \r
-                               /* tranform into axis space */\r
-                               m4x4_multiply_by_m4x4( transform, temp );\r
-                       }\r
-                       \r
-                       /* handle z-up models */\r
-                       else\r
-                       {\r
-                               /* set angles */\r
-                               VectorSet( angles, 0.0f, 0.0f, angle );\r
-                               \r
-                               /* set matrix */\r
-                               m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );\r
-                       }\r
-                       \r
-                       /* insert the model */\r
-                       InsertModel( (char *) model->model, 0, transform, NULL, ds->celShader, ds->entityNum, ds->castShadows, ds->recvShadows, 0, ds->lightmapScale );\r
-                       \r
-                       /* return to sender */\r
-                       return 1;\r
-               }\r
-       }\r
-       \r
-       /* split the longest edge and map it */\r
-       LerpDrawVert( tri[ max ], tri[ (max + 1) % 3 ], &mid );\r
-       \r
-       /* recurse to first triangle */\r
-       VectorCopy( tri, tri2 );\r
-       tri2[ max ] = &mid;\r
-       n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );\r
-       if( n < 0 )\r
-               return n;\r
-       localNumSurfaceModels += n;\r
-       \r
-       /* recurse to second triangle */\r
-       VectorCopy( tri, tri2 );\r
-       tri2[ (max + 1) % 3 ] = &mid;\r
-       n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );\r
-       if( n < 0 )\r
-               return n;\r
-       localNumSurfaceModels += n;\r
-       \r
-       /* return count */\r
-       return localNumSurfaceModels;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddSurfaceModels()\r
-adds a surface's shader models to the surface\r
-*/\r
-\r
-int AddSurfaceModels( mapDrawSurface_t *ds )\r
-{\r
-       surfaceModel_t  *model;\r
-       int                             i, x, y, n, pw[ 5 ], r, localNumSurfaceModels, iterations;\r
-       mesh_t                  src, *mesh, *subdivided;\r
-       bspDrawVert_t   centroid, *tri[ 3 ];\r
-       float                   alpha;\r
-       \r
-       \r
-       /* dummy check */\r
-       if( ds == NULL || ds->shaderInfo == NULL || ds->shaderInfo->surfaceModel == NULL )\r
-               return 0;\r
-       \r
-       /* init */\r
-       localNumSurfaceModels = 0;\r
-       \r
-       /* walk the model list */\r
-       for( model = ds->shaderInfo->surfaceModel; model != NULL; model = model->next )\r
-       {\r
-               /* switch on type */\r
-               switch( ds->type )\r
-               {\r
-                       /* handle brush faces and decals */\r
-                       case SURFACE_FACE:\r
-                       case SURFACE_DECAL:\r
-                               /* calculate centroid */\r
-                               memset( &centroid, 0, sizeof( centroid ) );\r
-                               alpha = 0.0f;\r
-                               \r
-                               /* walk verts */\r
-                               for( i = 0; i < ds->numVerts; i++ )\r
-                               {\r
-                                       VectorAdd( centroid.xyz, ds->verts[ i ].xyz, centroid.xyz );\r
-                                       VectorAdd( centroid.normal, ds->verts[ i ].normal, centroid.normal );\r
-                                       centroid.st[ 0 ] += ds->verts[ i ].st[ 0 ];\r
-                                       centroid.st[ 1 ] += ds->verts[ i ].st[ 1 ];\r
-                                       alpha += ds->verts[ i ].color[ 0 ][ 3 ];\r
-                               }\r
-                               \r
-                               /* average */\r
-                               centroid.xyz[ 0 ] /= ds->numVerts;\r
-                               centroid.xyz[ 1 ] /= ds->numVerts;\r
-                               centroid.xyz[ 2 ] /= ds->numVerts;\r
-                               if( VectorNormalize( centroid.normal, centroid.normal ) == 0.0f )\r
-                                       VectorCopy( ds->verts[ 0 ].normal, centroid.normal );\r
-                               centroid.st[ 0 ]  /= ds->numVerts;\r
-                               centroid.st[ 1 ]  /= ds->numVerts;\r
-                               alpha /= ds->numVerts;\r
-                               centroid.color[ 0 ][ 0 ] = 0xFF;\r
-                               centroid.color[ 0 ][ 1 ] = 0xFF;\r
-                               centroid.color[ 0 ][ 2 ] = 0xFF;\r
-                               centroid.color[ 0 ][ 2 ] = (alpha > 255.0f ? 0xFF : alpha);\r
-                               \r
-                               /* head vert is centroid */\r
-                               tri[ 0 ] = &centroid;\r
-                               \r
-                               /* walk fanned triangles */\r
-                               for( i = 0; i < ds->numVerts; i++ )\r
-                               {\r
-                                       /* set triangle */\r
-                                       tri[ 1 ] = &ds->verts[ i ];\r
-                                       tri[ 2 ] = &ds->verts[ (i + 1) % ds->numVerts ];\r
-                                       \r
-                                       /* create models */\r
-                                       n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
-                                       if( n < 0 )\r
-                                               return n;\r
-                                       localNumSurfaceModels += n;\r
-                               }\r
-                               break;\r
-                       \r
-                       /* handle patches */\r
-                       case SURFACE_PATCH:\r
-                               /* subdivide the surface */\r
-                               src.width = ds->patchWidth;\r
-                               src.height = ds->patchHeight;\r
-                               src.verts = ds->verts;\r
-                               //%     subdivided = SubdivideMesh( src, 8.0f, 512 );\r
-                               iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );\r
-                               subdivided = SubdivideMesh2( src, iterations );\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
-                               \r
-                               /* subdivide each quad to place the models */\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
-                                               /* triangle 1 */\r
-                                               tri[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];\r
-                                               tri[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];\r
-                                               tri[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];\r
-                                               n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
-                                               if( n < 0 )\r
-                                                       return n;\r
-                                               localNumSurfaceModels += n;\r
-                                               \r
-                                               /* triangle 2 */\r
-                                               tri[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];\r
-                                               tri[ 1 ] = &mesh->verts[ pw[ r + 2 ] ];\r
-                                               tri[ 2 ] = &mesh->verts[ pw[ r + 3 ] ];\r
-                                               n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
-                                               if( n < 0 )\r
-                                                       return n;\r
-                                               localNumSurfaceModels += n;\r
-                                       }\r
-                               }\r
-                               \r
-                               /* free the subdivided mesh */\r
-                               FreeMesh( mesh );\r
-                               break;\r
-                       \r
-                       /* handle triangle surfaces */\r
-                       case SURFACE_TRIANGLES:\r
-                       case SURFACE_FORCED_META:\r
-                       case SURFACE_META:\r
-                               /* walk the triangle list */\r
-                               for( i = 0; i < ds->numIndexes; i += 3 )\r
-                               {\r
-                                       tri[ 0 ] = &ds->verts[ ds->indexes[ i ] ];\r
-                                       tri[ 1 ] = &ds->verts[ ds->indexes[ i + 1 ] ];\r
-                                       tri[ 2 ] = &ds->verts[ ds->indexes[ i + 2 ] ];\r
-                                       n = AddSurfaceModelsToTriangle_r( ds, model, tri );\r
-                                       if( n < 0 )\r
-                                               return n;\r
-                                       localNumSurfaceModels += n;\r
-                               }\r
-                               break;\r
-                       \r
-                       /* no support for flares, foghull, etc */\r
-                       default:\r
-                               break;\r
-               }\r
-       }\r
-       \r
-       /* return count */\r
-       return localNumSurfaceModels;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddEntitySurfaceModels() - ydnar\r
-adds surfacemodels to an entity's surfaces\r
-*/\r
-\r
-void AddEntitySurfaceModels( entity_t *e )\r
-{\r
-       int             i;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- AddEntitySurfaceModels ---\n" );\r
-       \r
-       /* walk the surface list */\r
-       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )\r
-               numSurfaceModels += AddSurfaceModels( &mapDrawSurfs[ i ] );\r
-}\r
-\r
-\r
-\r
-/*\r
-FilterDrawsurfsIntoTree()\r
-upon completion, all drawsurfs that actually generate a reference\r
-will have been emited to the bspfile arrays, and the references\r
-will have valid final indexes\r
-*/\r
-\r
-void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )\r
-{\r
-       int                                     i, j;\r
-       mapDrawSurface_t        *ds;\r
-       shaderInfo_t            *si;\r
-       vec3_t                          origin, mins, maxs;\r
-       int                                     refs;\r
-       int                                     numSurfs, numRefs, numSkyboxSurfaces;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- FilterDrawsurfsIntoTree ---\n" );\r
-       \r
-       /* filter surfaces into the tree */\r
-       numSurfs = 0;\r
-       numRefs = 0;\r
-       numSkyboxSurfaces = 0;\r
-       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )\r
-       {\r
-               /* get surface and try to early out */\r
-               ds = &mapDrawSurfs[ i ];\r
-               if( ds->numVerts == 0 && ds->type != SURFACE_FLARE && ds->type != SURFACE_SHADER )\r
-                       continue;\r
-               \r
-               /* get shader */\r
-               si = ds->shaderInfo;\r
-               \r
-               /* ydnar: skybox surfaces are special */\r
-               if( ds->skybox )\r
-               {\r
-                       refs = AddReferenceToTree_r( ds, tree->headnode, qtrue );\r
-                       ds->skybox = qfalse;\r
-               }\r
-               else\r
-               {\r
-                       /* refs initially zero */\r
-                       refs = 0;\r
-                       \r
-                       /* ydnar: apply alphamod */\r
-                       AlphaMod( ds->shaderInfo->alphaMod, ds->numVerts, ds->verts );\r
-                       \r
-                       /* apply texture coordinate mods */\r
-                       for( j = 0; j < ds->numVerts; j++ )\r
-                               TcMod( si->mod, ds->verts[ j ].st );\r
-                       \r
-                       /* ydnar: make fur surfaces */\r
-                       if( si->furNumLayers > 0 )\r
-                               Fur( ds );\r
-                       \r
-                       /* ydnar/sd: make foliage surfaces */\r
-                       if( si->foliage != NULL )\r
-                               Foliage( ds );\r
-                       \r
-                       /* create a flare surface if necessary */\r
-                       if( si->flareShader[ 0 ] )\r
-                               AddSurfaceFlare( ds, e->origin );\r
-                       \r
-                       /* ydnar: don't emit nodraw surfaces (like nodraw fog) */\r
-                       if( si != NULL && (si->compileFlags & C_NODRAW) && ds->type != SURFACE_PATCH )\r
-                               continue;\r
-                       \r
-                       /* ydnar: bias the surface textures */\r
-                       BiasSurfaceTextures( ds );\r
-                       \r
-                       /* ydnar: globalizing of fog volume handling (eek a hack) */\r
-                       if( e != entities && si->noFog == qfalse )\r
-                       {\r
-                               /* find surface origin and offset by entity origin */\r
-                               VectorAdd( ds->mins, ds->maxs, origin );\r
-                               VectorScale( origin, 0.5f, origin );\r
-                               VectorAdd( origin, e->origin, origin );\r
-                               \r
-                               VectorAdd( ds->mins, e->origin, mins );\r
-                               VectorAdd( ds->maxs, e->origin, maxs );\r
-                               \r
-                               /* set the fog number for this surface */\r
-                               ds->fogNum = FogForBounds( mins, maxs, 1.0f );  //%     FogForPoint( origin, 0.0f );\r
-                       }\r
-               }\r
-               \r
-               /* ydnar: gs mods: handle the various types of surfaces */\r
-               switch( ds->type )\r
-               {\r
-                       /* handle brush faces */\r
-                       case SURFACE_FACE:\r
-                       case SURFACE_DECAL:\r
-                               if( refs == 0 )\r
-                                       refs = FilterFaceIntoTree( ds, tree );\r
-                               if( refs > 0 )\r
-                                       EmitFaceSurface( ds );\r
-                               break;\r
-                       \r
-                       /* handle patches */\r
-                       case SURFACE_PATCH:\r
-                               if( refs == 0 )\r
-                                       refs = FilterPatchIntoTree( ds, tree );\r
-                               if( refs > 0 )\r
-                                       EmitPatchSurface( ds );\r
-                               break;\r
-                       \r
-                       /* handle triangle surfaces */\r
-                       case SURFACE_TRIANGLES:\r
-                       case SURFACE_FORCED_META:\r
-                       case SURFACE_META:\r
-                               //%     Sys_FPrintf( SYS_VRB, "Surface %4d: [%1d] %4d verts %s\n", numSurfs, ds->planar, ds->numVerts, si->shader );\r
-                               if( refs == 0 )\r
-                                       refs = FilterTrianglesIntoTree( ds, tree );\r
-                               if( refs > 0 )\r
-                                       EmitTriangleSurface( ds );\r
-                               break;\r
-                       \r
-                       /* handle foliage surfaces (splash damage/wolf et) */\r
-                       case SURFACE_FOLIAGE:\r
-                               //%     Sys_FPrintf( SYS_VRB, "Surface %4d: [%d] %4d verts %s\n", numSurfs, ds->numFoliageInstances, ds->numVerts, si->shader );\r
-                               if( refs == 0 )\r
-                                       refs = FilterFoliageIntoTree( ds, tree );\r
-                               if( refs > 0 )\r
-                                       EmitTriangleSurface( ds );\r
-                               break;\r
-                       \r
-                       /* handle foghull surfaces */\r
-                       case SURFACE_FOGHULL:\r
-                               if( refs == 0 )\r
-                                       refs = AddReferenceToTree_r( ds, tree->headnode, qfalse );\r
-                               if( refs > 0 )\r
-                                       EmitTriangleSurface( ds );\r
-                               break;\r
-                       \r
-                       /* handle flares */\r
-                       case SURFACE_FLARE:\r
-                               if( refs == 0 )\r
-                                       refs = FilterFlareSurfIntoTree( ds, tree );\r
-                               if( refs > 0 )\r
-                                       EmitFlareSurface( ds );\r
-                               break;\r
-                       \r
-                       /* handle shader-only surfaces */\r
-                       case SURFACE_SHADER:\r
-                               refs = 1;\r
-                               EmitFlareSurface( ds );\r
-                               break;\r
-                       \r
-                       /* no references */\r
-                       default:\r
-                               refs = 0;\r
-                               break;\r
-               }\r
-               \r
-               /* tot up the references */\r
-               if( refs > 0 )\r
-               {\r
-                       /* tot up counts */\r
-                       numSurfs++;\r
-                       numRefs += refs;\r
-                       \r
-                       /* emit extra surface data */\r
-                       SetSurfaceExtra( ds, numBSPDrawSurfaces - 1 );\r
-                       //%     Sys_FPrintf( SYS_VRB, "%d verts %d indexes\n", ds->numVerts, ds->numIndexes );\r
-                       \r
-                       /* one last sanity check */\r
-                       {\r
-                               bspDrawSurface_t        *out;\r
-                               out = &bspDrawSurfaces[ numBSPDrawSurfaces - 1 ];\r
-                               if( out->numVerts == 3 && out->numIndexes > 3 )\r
-                               {\r
-                                       Sys_Printf( "\nWARNING: Potentially bad %s surface (%d: %d, %d)\n     %s\n",\r
-                                               surfaceTypes[ ds->type ],\r
-                                               numBSPDrawSurfaces - 1, out->numVerts, out->numIndexes, si->shader );\r
-                               }\r
-                       }\r
-                       \r
-                       /* ydnar: handle skybox surfaces */\r
-                       if( ds->skybox )\r
-                       {\r
-                               MakeSkyboxSurface( ds );\r
-                               numSkyboxSurfaces++;\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* emit some statistics */\r
-       Sys_FPrintf( SYS_VRB, "%9d references\n", numRefs );\r
-       Sys_FPrintf( SYS_VRB, "%9d (%d) emitted drawsurfs\n", numSurfs, numBSPDrawSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d stripped face surfaces\n", numStripSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d fanned face surfaces\n", numFanSurfaces );\r
-       Sys_FPrintf( SYS_VRB, "%9d surface models generated\n", numSurfaceModels );\r
-       Sys_FPrintf( SYS_VRB, "%9d skybox surfaces generated\n", numSkyboxSurfaces );\r
-       for( i = 0; i < NUM_SURFACE_TYPES; i++ )\r
-               Sys_FPrintf( SYS_VRB, "%9d %s surfaces\n", numSurfacesByType[ i ], surfaceTypes[ i ] );\r
-       \r
-       Sys_FPrintf( SYS_VRB, "%9d redundant indexes supressed, saving %d Kbytes\n", numRedundantIndexes, (numRedundantIndexes * 4 / 1024) );\r
-}\r
-\r
-\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_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+/*
+
+this section handles drawsurface allocation and creation
+
+*/
+
+/*
+AllocDrawSurface()
+ydnar: gs mods: changed to force an explicit type when allocating
+*/
+
+mapDrawSurface_t *AllocDrawSurface( surfaceType_t type )
+{
+       mapDrawSurface_t        *ds;
+       
+       
+       /* ydnar: gs mods: only allocate valid types */
+       if( type <= SURFACE_BAD || type >= NUM_SURFACE_TYPES )
+               Error( "AllocDrawSurface: Invalid surface type %d specified", type );
+       
+       /* bounds check */
+       if( numMapDrawSurfs >= MAX_MAP_DRAW_SURFS )
+               Error( "MAX_MAP_DRAW_SURFS (%d) exceeded", MAX_MAP_DRAW_SURFS );
+       ds = &mapDrawSurfs[ numMapDrawSurfs ];
+       numMapDrawSurfs++;
+       
+       /* ydnar: do initial surface setup */
+       memset( ds, 0, sizeof( mapDrawSurface_t ) );
+       ds->type = type;
+       ds->planeNum = -1;
+       ds->fogNum = defaultFogNum;                             /* ydnar 2003-02-12 */
+       ds->outputNum = -1;                                             /* ydnar 2002-08-13 */
+       ds->surfaceNum = numMapDrawSurfs - 1;   /* ydnar 2003-02-16 */
+       
+       return ds;
+}
+
+
+
+/*
+FinishSurface()
+ydnar: general surface finish pass
+*/
+
+void FinishSurface( mapDrawSurface_t *ds )
+{
+       /* dummy check */
+       if( ds == NULL || ds->shaderInfo == NULL )
+               return;
+       
+       /* ydnar: rocking tek-fu celshading */
+       if( ds->celShader != NULL )
+               MakeCelSurface( ds, ds->celShader );
+       
+       /* ydnar: rocking surface cloning (fur baby yeah!) */
+       if( ds->shaderInfo->cloneShader[ 0 ] != '\0' )
+               CloneSurface( ds, ShaderInfoForShader( ds->shaderInfo->cloneShader ) );
+}
+
+
+
+/*
+CloneSurface()
+clones a map drawsurface, using the specified shader
+*/
+
+mapDrawSurface_t *CloneSurface( mapDrawSurface_t *src, shaderInfo_t *si )
+{
+       mapDrawSurface_t        *ds;
+       
+       
+       /* dummy check */
+       if( src == NULL || si == NULL )
+               return NULL;
+       
+       /* allocate a new surface */
+       ds = AllocDrawSurface( src->type );
+       if( ds == NULL )
+               return NULL;
+       
+       /* copy it */
+       memcpy( ds, src, sizeof( *ds ) );
+       
+       /* destroy side reference */
+       ds->sideRef = NULL;
+       
+       /* set shader */
+       ds->shaderInfo = si;
+       
+       /* copy verts */
+       if( ds->numVerts > 0 )
+       {
+               ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
+               memcpy( ds->verts, src->verts, ds->numVerts * sizeof( *ds->verts ) );
+       }
+       
+       /* copy indexes */
+       if( ds->numIndexes <= 0 )
+               return ds;
+       ds->indexes = safe_malloc( ds->numIndexes * sizeof( *ds->indexes ) );
+       memcpy( ds->indexes, src->indexes, ds->numIndexes * sizeof( *ds->indexes ) );
+       
+       /* return the surface */
+       return ds;
+}
+
+
+
+/*
+MakeCelSurface() - ydnar
+makes a copy of a surface, but specific to cel shading
+*/
+
+mapDrawSurface_t *MakeCelSurface( mapDrawSurface_t *src, shaderInfo_t *si )
+{
+       mapDrawSurface_t        *ds;
+       
+       
+       /* dummy check */
+       if( src == NULL || si == NULL )
+               return NULL;
+       
+       /* don't create cel surfaces for certain types of shaders */
+       if( (src->shaderInfo->compileFlags & C_TRANSLUCENT) ||
+               (src->shaderInfo->compileFlags & C_SKY) )
+               return NULL;
+       
+       /* make a copy */
+       ds = CloneSurface( src, si );
+       if( ds == NULL )
+               return NULL;
+       
+       /* do some fixups for celshading */
+       ds->planar = qfalse;
+       ds->planeNum = -1;
+       
+       /* return the surface */
+       return ds;
+}
+
+
+
+/*
+MakeSkyboxSurface() - ydnar
+generates a skybox surface, viewable from everywhere there is sky
+*/
+
+mapDrawSurface_t *MakeSkyboxSurface( mapDrawSurface_t *src )
+{
+       int                                     i;
+       mapDrawSurface_t        *ds;
+       
+       
+       /* dummy check */
+       if( src == NULL )
+               return NULL;
+       
+       /* make a copy */
+       ds = CloneSurface( src, src->shaderInfo );
+       if( ds == NULL )
+               return NULL;
+       
+       /* set parent */
+       ds->parent = src;
+       
+       /* scale the surface vertexes */
+       for( i = 0; i < ds->numVerts; i++ )
+       {
+               m4x4_transform_point( skyboxTransform, ds->verts[ i ].xyz );
+               
+               /* debug code */
+               //%     bspDrawVerts[ bspDrawSurfaces[ ds->outputNum ].firstVert + i ].color[ 0 ][ 1 ] = 0;
+               //%     bspDrawVerts[ bspDrawSurfaces[ ds->outputNum ].firstVert + i ].color[ 0 ][ 2 ] = 0;
+       }
+       
+       /* so backface culling creep doesn't bork the surface */
+       VectorClear( ds->lightmapVecs[ 2 ] );
+       
+       /* return the surface */
+       return ds;
+}
+
+
+
+/*
+IsTriangleDegenerate
+returns qtrue if all three points are colinear, backwards, or the triangle is just plain bogus
+*/
+
+#define        TINY_AREA       1.0f
+
+qboolean IsTriangleDegenerate( bspDrawVert_t *points, int a, int b, int c )
+{
+       vec3_t          v1, v2, v3;
+       float           d;
+       
+       
+       /* calcuate the area of the triangle */
+       VectorSubtract( points[ b ].xyz, points[ a ].xyz, v1 );
+       VectorSubtract( points[ c ].xyz, points[ a ].xyz, v2 );
+       CrossProduct( v1, v2, v3 );
+       d = VectorLength( v3 );
+       
+       /* assume all very small or backwards triangles will cause problems */
+       if( d < TINY_AREA )
+               return qtrue;
+       
+       /* must be a good triangle */
+       return qfalse;
+}
+
+
+
+/*
+ClearSurface() - ydnar
+clears a surface and frees any allocated memory
+*/
+
+void ClearSurface( mapDrawSurface_t *ds )
+{
+       ds->type = SURFACE_BAD;
+       ds->planar = qfalse;
+       ds->planeNum = -1;
+       ds->numVerts = 0;
+       if( ds->verts != NULL )
+               free( ds->verts );
+       ds->verts = NULL;
+       ds->numIndexes = 0;
+       if( ds->indexes != NULL )
+               free( ds->indexes );
+       ds->indexes = NULL;
+       numClearedSurfaces++;
+}
+
+
+
+/*
+TidyEntitySurfaces() - ydnar
+deletes all empty or bad surfaces from the surface list
+*/
+
+void TidyEntitySurfaces( entity_t *e )
+{
+       int                                     i, j, deleted;
+       mapDrawSurface_t        *out, *in;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- TidyEntitySurfaces ---\n" );
+       
+       /* walk the surface list */
+       deleted = 0;
+       for( i = e->firstDrawSurf, j = e->firstDrawSurf; j < numMapDrawSurfs; i++, j++ )
+       {
+               /* get out surface */
+               out = &mapDrawSurfs[ i ];
+               
+               /* walk the surface list again until a proper surface is found */
+               for( j; j < numMapDrawSurfs; j++ )
+               {
+                       /* get in surface */
+                       in = &mapDrawSurfs[ j ];
+                       
+                       /* this surface ok? */
+                       if( in->type == SURFACE_FLARE || in->type == SURFACE_SHADER ||
+                               (in->type != SURFACE_BAD && in->numVerts > 0) )
+                               break;
+                       
+                       /* nuke it */
+                       ClearSurface( in );
+                       deleted++;
+               }
+               
+               /* copy if necessary */
+               if( i != j )
+                       memcpy( out, in, sizeof( mapDrawSurface_t ) );
+       }
+       
+       /* set the new number of drawsurfs */
+       numMapDrawSurfs = i;
+       
+       /* emit some stats */
+       Sys_FPrintf( SYS_VRB, "%9d empty or malformed surfaces deleted\n", deleted );
+}
+
+
+
+/*
+CalcSurfaceTextureRange() - ydnar
+calculates the clamped texture range for a given surface, returns qtrue if it's within [-texRange,texRange]
+*/
+
+qboolean CalcSurfaceTextureRange( mapDrawSurface_t *ds )
+{
+       int             i, j, v, size[ 2 ];
+       float   mins[ 2 ], maxs[ 2 ];
+       
+       
+       /* try to early out */
+       if( ds->numVerts <= 0 )
+               return qtrue;
+       
+       /* walk the verts and determine min/max st values */
+       mins[ 0 ] = 999999;
+       mins[ 1 ] = 999999;
+       maxs[ 0 ] = -999999;
+       maxs[ 1 ] = -999999;
+       for( i = 0; i < ds->numVerts; i++ )
+       {
+               for( j = 0; j < 2; j++ )
+               {
+                       if( ds->verts[ i ].st[ j ] < mins[ j ] )
+                               mins[ j ] = ds->verts[ i ].st[ j ];
+                       if( ds->verts[ i ].st[ j ] > maxs[ j ] )
+                               maxs[ j ] = ds->verts[ i ].st[ j ];
+               }
+       }
+       
+       /* clamp to integer range and calculate surface bias values */
+       for( j = 0; j < 2; j++ )
+               ds->bias[ j ] = -floor( 0.5f * (mins[ j ] + maxs[ j ]) );
+       
+       /* find biased texture coordinate mins/maxs */
+       size[ 0 ] = ds->shaderInfo->shaderWidth;
+       size[ 1 ] = ds->shaderInfo->shaderHeight;
+       ds->texMins[ 0 ] = 999999;
+       ds->texMins[ 1 ] = 999999;
+       ds->texMaxs[ 0 ] = -999999;
+       ds->texMaxs[ 1 ] = -999999;
+       for( i = 0; i < ds->numVerts; i++ )
+       {
+               for( j = 0; j < 2; j++ )
+               {
+                       v = ((float) ds->verts[ i ].st[ j ] + ds->bias[ j ]) * size[ j ];
+                       if( v < ds->texMins[ j ] )
+                               ds->texMins[ j ] = v;
+                       if( v > ds->texMaxs[ j ] )
+                               ds->texMaxs[ j ] = v;
+               }
+       }
+       
+       /* calc ranges */
+       for( j = 0; j < 2; j++ )
+               ds->texRange[ j ] = (ds->texMaxs[ j ] - ds->texMins[ j ]);
+       
+       /* if range is zero, then assume unlimited precision */
+       if( texRange == 0 )
+               return qtrue;
+       
+       /* within range? */
+       for( j = 0; j < 2; j++ )
+       {
+               if( ds->texMins[ j ] < -texRange || ds->texMaxs[ j ] > texRange )
+                       return qfalse;
+       }
+       
+       /* within range */
+       return qtrue;
+}
+
+
+
+/*
+CalcLightmapAxis() - ydnar
+gives closed lightmap axis for a plane normal
+*/
+
+qboolean CalcLightmapAxis( vec3_t normal, vec3_t axis )
+{
+       vec3_t  absolute;
+               
+       
+       /* test */
+       if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && normal[ 2 ] == 0.0f )
+       {
+               VectorClear( axis );
+               return qfalse;
+       }
+       
+       /* get absolute normal */
+       absolute[ 0 ] = fabs( normal[ 0 ] );
+       absolute[ 1 ] = fabs( normal[ 1 ] );
+       absolute[ 2 ] = fabs( normal[ 2 ] );
+       
+       /* test and set */
+       if( absolute[ 2 ] > absolute[ 0 ] - 0.0001f && absolute[ 2 ] > absolute[ 1 ] - 0.0001f )
+       {
+               if( normal[ 2 ] > 0.0f )
+                       VectorSet( axis, 0.0f, 0.0f, 1.0f );
+               else
+                       VectorSet( axis, 0.0f, 0.0f, -1.0f );
+       }
+       else if( absolute[ 0 ] > absolute[ 1 ] - 0.0001f && absolute[ 0 ] > absolute[ 2 ] - 0.0001f )
+       {
+               if( normal[ 0 ] > 0.0f )
+                       VectorSet( axis, 1.0f, 0.0f, 0.0f );
+               else
+                       VectorSet( axis, -1.0f, 0.0f, 0.0f );
+       }
+       else
+       {
+               if( normal[ 1 ] > 0.0f )
+                       VectorSet( axis, 0.0f, 1.0f, 0.0f );
+               else
+                       VectorSet( axis, 0.0f, -1.0f, 0.0f );
+       }
+       
+       /* return ok */
+       return qtrue;
+}
+
+
+
+/*
+ClassifySurfaces() - ydnar
+fills out a bunch of info in the surfaces, including planar status, lightmap projection, and bounding box
+*/
+
+#define PLANAR_EPSILON 0.5f    //% 0.126f 0.25f
+
+void ClassifySurfaces( int numSurfs, mapDrawSurface_t *ds )
+{
+       int                                     i, bestAxis;
+       float                           dist;
+       vec4_t                          plane;
+       shaderInfo_t            *si;
+       static vec3_t           axii[ 6 ] =
+                                               {
+                                                       { 0, 0, -1 },
+                                                       { 0, 0, 1 },
+                                                       { -1, 0, 0 },
+                                                       { 1, 0, 0 },
+                                                       { 0, -1, 0 },
+                                                       { 0, 1, 0 }
+                                               };
+       
+       
+       /* walk the list of surfaces */
+       for( numSurfs; numSurfs > 0; numSurfs--, ds++ )
+       {
+               /* ignore bogus (or flare) surfaces */
+               if( ds->type == SURFACE_BAD || ds->numVerts <= 0 )
+                       continue;
+               
+               /* get shader */
+               si = ds->shaderInfo;
+               
+               /* -----------------------------------------------------------------
+                  force meta if vertex count is too high or shader requires it
+                  ----------------------------------------------------------------- */
+               
+               if( ds->type != SURFACE_PATCH && ds->type != SURFACE_FACE )
+               {
+                       if( ds->numVerts > SHADER_MAX_VERTEXES )
+                               ds->type = SURFACE_FORCED_META;
+               }
+               
+               /* -----------------------------------------------------------------
+                  plane and bounding box classification 
+                  ----------------------------------------------------------------- */
+               
+               /* set surface bounding box */
+               ClearBounds( ds->mins, ds->maxs );
+               for( i = 0; i < ds->numVerts; i++ )
+                       AddPointToBounds( ds->verts[ i ].xyz, ds->mins, ds->maxs );
+               
+               /* try to get an existing plane */
+               if( ds->planeNum >= 0 )
+               {
+                       VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
+                       plane[ 3 ] = mapplanes[ ds->planeNum ].dist;
+               }
+               
+               /* construct one from the first vert with a valid normal */
+               else
+               {
+                       VectorClear( plane );
+                       plane[ 3 ] = 0.0f;
+                       for( i = 0; i < ds->numVerts; i++ )
+                       {
+                               if( ds->verts[ i ].normal[ 0 ] != 0.0f && ds->verts[ i ].normal[ 1 ] != 0.0f && ds->verts[ i ].normal[ 2 ] != 0.0f )
+                               {
+                                       VectorCopy( ds->verts[ i ].normal, plane );
+                                       plane[ 3 ] = DotProduct( ds->verts[ i ].xyz, plane );
+                                       break;
+                               }
+                       }
+               }
+               
+               /* test for bogus plane */
+               if( VectorLength( plane ) <= 0.0f )
+               {
+                       ds->planar = qfalse;
+                       ds->planeNum = -1;
+               }
+               else
+               {
+                       /* determine if surface is planar */
+                       ds->planar = qtrue;
+                       
+                       /* test each vert */
+                       for( i = 0; i < ds->numVerts; i++ )
+                       {
+                               /* point-plane test */
+                               dist = DotProduct( ds->verts[ i ].xyz, plane ) - plane[ 3 ];
+                               if( fabs( dist ) > PLANAR_EPSILON )
+                               {
+                                       //%     if( ds->planeNum >= 0 )
+                                       //%     {
+                                       //%             Sys_Printf( "WARNING: Planar surface marked unplanar (%f > %f)\n", fabs( dist ), PLANAR_EPSILON );
+                                       //%             ds->verts[ i ].color[ 0 ][ 0 ] = ds->verts[ i ].color[ 0 ][ 2 ] = 0;
+                                       //%     }
+                                       ds->planar = qfalse;
+                                       break;
+                               }
+                       }
+               }
+               
+               /* find map plane if necessary */
+               if( ds->planar )
+               {
+                       if( ds->planeNum < 0 )
+                               ds->planeNum = FindFloatPlane( plane, plane[ 3 ], 1, &ds->verts[ 0 ].xyz );
+                       VectorCopy( plane, ds->lightmapVecs[ 2 ] );
+               }
+               else
+               {
+                       ds->planeNum = -1;
+                       VectorClear( ds->lightmapVecs[ 2 ] );
+                       //% if( ds->type == SURF_META || ds->type == SURF_FACE )
+                       //%             Sys_Printf( "WARNING: Non-planar face (%d): %s\n", ds->planeNum, ds->shaderInfo->shader );
+               }
+               
+               /* -----------------------------------------------------------------
+                  lightmap bounds and axis projection
+                  ----------------------------------------------------------------- */
+               
+               /* vertex lit surfaces don't need this information */
+               if( si->compileFlags & C_VERTEXLIT || ds->type == SURFACE_TRIANGLES )
+               {
+                       VectorClear( ds->lightmapAxis );
+                       //%     VectorClear( ds->lightmapVecs[ 2 ] );
+                       ds->sampleSize = 0;
+                       continue;
+               }
+               
+               /* the shader can specify an explicit lightmap axis */
+               if( si->lightmapAxis[ 0 ] || si->lightmapAxis[ 1 ] || si->lightmapAxis[ 2 ] )
+                       VectorCopy( si->lightmapAxis, ds->lightmapAxis );
+               else if( ds->type == SURFACE_FORCED_META )
+                       VectorClear( ds->lightmapAxis );
+               else if( ds->planar )
+                       CalcLightmapAxis( plane, ds->lightmapAxis );
+               else
+               {
+                       /* find best lightmap axis */
+                       for( bestAxis = 0; bestAxis < 6; bestAxis++ )
+                       {
+                               for( i = 0; i < ds->numVerts && bestAxis < 6; i++ )
+                               {
+                                       //% Sys_Printf( "Comparing %1.3f %1.3f %1.3f to %1.3f %1.3f %1.3f\n",
+                                       //%     ds->verts[ i ].normal[ 0 ], ds->verts[ i ].normal[ 1 ], ds->verts[ i ].normal[ 2 ],
+                                       //%     axii[ bestAxis ][ 0 ], axii[ bestAxis ][ 1 ], axii[ bestAxis ][ 2 ] );
+                                       if( DotProduct( ds->verts[ i ].normal, axii[ bestAxis ] ) < 0.25f )     /* fixme: adjust this tolerance to taste */
+                                               break;
+                               }
+                               
+                               if( i == ds->numVerts )
+                                       break;
+                       }
+                       
+                       /* set axis if possible */
+                       if( bestAxis < 6 )
+                       {
+                               //% if( ds->type == SURFACE_PATCH )
+                               //%     Sys_Printf( "Mapped axis %d onto patch\n", bestAxis );
+                               VectorCopy( axii[ bestAxis ], ds->lightmapAxis );
+                       }
+                       
+                       /* debug code */
+                       //% if( ds->type == SURFACE_PATCH )
+                       //%     Sys_Printf( "Failed to map axis %d onto patch\n", bestAxis );
+               }
+               
+               /* get lightmap sample size */
+               if( ds->sampleSize <= 0 )
+               {
+                       ds->sampleSize = sampleSize;
+                       if( ds->shaderInfo->lightmapSampleSize )
+                               ds->sampleSize = ds->shaderInfo->lightmapSampleSize;
+                       if( ds->lightmapScale > 0 )
+                               ds->sampleSize *= ds->lightmapScale;
+                       if( ds->sampleSize <= 0 )
+                               ds->sampleSize = 1;
+                       else if( ds->sampleSize > 16384 )       /* powers of 2 are preferred */
+                               ds->sampleSize = 16384;
+               }
+       }
+}
+
+
+
+/*
+ClassifyEntitySurfaces() - ydnar
+classifies all surfaces in an entity
+*/
+
+void ClassifyEntitySurfaces( entity_t *e )
+{
+       int             i;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- ClassifyEntitySurfaces ---\n" );
+       
+       /* walk the surface list */
+       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
+               ClassifySurfaces( 1, &mapDrawSurfs[ i ] );
+       
+       /* tidy things up */
+       TidyEntitySurfaces( e );
+}
+
+
+
+/*
+GetShaderIndexForPoint() - ydnar
+for shader-indexed surfaces (terrain), find a matching index from the indexmap
+*/
+
+byte GetShaderIndexForPoint( indexMap_t *im, vec3_t eMins, vec3_t eMaxs, vec3_t point )
+{
+       int                     i, x, y;
+       float           s, t;
+       vec3_t          mins, maxs, size;
+       
+       
+       /* early out if no indexmap */
+       if( im == NULL )
+               return 0;
+       
+       /* this code is really broken */
+       #if 0
+               /* legacy precision fudges for terrain */
+               for( i = 0; i < 3; i++ )
+               {
+                       mins[ i ] = floor( eMins[ i ] + 0.1 );
+                       maxs[ i ] = floor( eMaxs[ i ] + 0.1 );
+                       size[ i ] = maxs[ i ] - mins[ i ];
+               }
+               
+               /* find st (fixme: support more than just z-axis projection) */
+               s = floor( point[ 0 ] + 0.1f - mins[ 0 ] ) / size[ 0 ];
+               t = floor( maxs[ 1 ] - point[ 1 ] + 0.1f ) / size[ 1 ];
+               if( s < 0.0f )
+                       s = 0.0f;
+               else if( s > 1.0f )
+                       s = 1.0f;
+               if( t < 0.0f )
+                       t = 0.0f;
+               else if( t > 1.0f )
+                       t = 1.0f;
+               
+               /* make xy */
+               x = (im->w - 1) * s;
+               y = (im->h - 1) * t;
+       #else
+               /* get size */
+               for( i = 0; i < 3; i++ )
+               {
+                       mins[ i ] = eMins[ i ];
+                       maxs[ i ] = eMaxs[ i ];
+                       size[ i ] = maxs[ i ] - mins[ i ];
+               }
+               
+               /* calc st */
+               s = (point[ 0 ] - mins[ 0 ]) / size[ 0 ];
+               t = (maxs[ 1 ] - point[ 1 ]) / size[ 1 ];
+               
+               /* calc xy */
+               x = s * im->w;
+               y = t * im->h;
+               if( x < 0 )
+                       x = 0;
+               else if( x > (im->w - 1) )
+                       x = (im->w - 1);
+               if( y < 0 )
+                       y = 0;
+               else if( y > (im->h - 1) )
+                       y = (im->h - 1);
+       #endif
+       
+       /* return index */
+       return im->pixels[ y * im->w + x ];
+}
+
+
+
+/*
+GetIndexedShader() - ydnar
+for a given set of indexes and an indexmap, get a shader and set the vertex alpha in-place
+this combines a couple different functions from terrain.c
+*/
+
+shaderInfo_t *GetIndexedShader( shaderInfo_t *parent, indexMap_t *im, int numPoints, byte *shaderIndexes )
+{
+       int                             i;
+       byte                    minShaderIndex, maxShaderIndex;
+       char                    shader[ MAX_QPATH ];
+       shaderInfo_t    *si;
+       
+       
+       /* early out if bad data */
+       if( im == NULL || numPoints <= 0 || shaderIndexes == NULL )
+               return ShaderInfoForShader( "default" );
+       
+       /* determine min/max index */
+       minShaderIndex = 255;
+       maxShaderIndex = 0;
+       for( i = 0; i < numPoints; i++ )
+       {
+               if( shaderIndexes[ i ] < minShaderIndex )
+                       minShaderIndex = shaderIndexes[ i ];
+               if( shaderIndexes[ i ] > maxShaderIndex )
+                       maxShaderIndex = shaderIndexes[ i ];
+       }
+       
+       /* set alpha inline */
+       for( i = 0; i < numPoints; i++ )
+       {
+               /* straight rip from terrain.c */
+               if( shaderIndexes[ i ] < maxShaderIndex )
+                       shaderIndexes[ i ] = 0;
+               else
+                       shaderIndexes[ i ] = 255;
+       }
+       
+       /* make a shader name */
+       if( minShaderIndex == maxShaderIndex )
+               sprintf( shader, "textures/%s_%d", im->shader, maxShaderIndex );
+       else
+               sprintf( shader, "textures/%s_%dto%d", im->shader, minShaderIndex, maxShaderIndex );
+       
+       /* get the shader */
+       si = ShaderInfoForShader( shader );
+       
+       /* inherit a few things from parent shader */
+       if( parent->globalTexture )
+               si->globalTexture = qtrue;
+       if( parent->forceMeta )
+               si->forceMeta = qtrue;
+       if( parent->nonplanar )
+               si->nonplanar = qtrue;
+       if( si->shadeAngleDegrees == 0.0 )
+               si->shadeAngleDegrees = parent->shadeAngleDegrees;
+       if( parent->tcGen && si->tcGen == qfalse )
+       {
+               /* set xy texture projection */
+               si->tcGen = qtrue;
+               VectorCopy( parent->vecs[ 0 ], si->vecs[ 0 ] );
+               VectorCopy( parent->vecs[ 1 ], si->vecs[ 1 ] );
+       }
+       if( VectorLength( parent->lightmapAxis ) > 0.0f && VectorLength( si->lightmapAxis ) <= 0.0f )
+       {
+               /* set lightmap projection axis */
+               VectorCopy( parent->lightmapAxis, si->lightmapAxis );
+       }
+       
+       /* return the shader */
+       return si;
+}
+
+
+
+
+/*
+DrawSurfaceForSide()
+creates a SURF_FACE drawsurface from a given brush side and winding
+*/
+
+#define        SNAP_FLOAT_TO_INT       8
+#define        SNAP_INT_TO_FLOAT       (1.0 / SNAP_FLOAT_TO_INT)
+
+mapDrawSurface_t *DrawSurfaceForSide( entity_t *e, brush_t *b, side_t *s, winding_t *w )
+{
+       int                                     i, j, k;
+       mapDrawSurface_t        *ds;
+       shaderInfo_t            *si, *parent;
+       bspDrawVert_t           *dv;
+       vec3_t                          texX, texY;
+       vec_t                           x, y;
+       vec3_t                          vTranslated;
+       qboolean                        indexed;
+       byte                            shaderIndexes[ 256 ];
+       float                           offsets[ 256 ];
+       char                            tempShader[ MAX_QPATH ];
+
+       
+       /* ydnar: don't make a drawsurf for culled sides */
+       if( s->culled )
+               return NULL;
+       
+       /* range check */
+       if( w->numpoints > MAX_POINTS_ON_WINDING )
+               Error( "DrawSurfaceForSide: w->numpoints = %d (> %d)", w->numpoints, MAX_POINTS_ON_WINDING );
+       
+       /* get shader */
+       si = s->shaderInfo;
+       
+       /* ydnar: gs mods: check for indexed shader */
+       if( si->indexed && b->im != NULL )
+       {
+               /* indexed */
+               indexed = qtrue;
+               
+               /* get shader indexes for each point */
+               for( i = 0; i < w->numpoints; i++ )
+               {
+                       shaderIndexes[ i ] = GetShaderIndexForPoint( b->im, b->eMins, b->eMaxs, w->p[ i ] );
+                       offsets[ i ] = b->im->offsets[ shaderIndexes[ i ] ];
+                       //%     Sys_Printf( "%f ", offsets[ i ] );
+               }
+               
+               /* get matching shader and set alpha */
+               parent = si;
+               si = GetIndexedShader( parent, b->im, w->numpoints, shaderIndexes );
+       }
+       else
+               indexed = qfalse;
+       
+       /* ydnar: sky hack/fix for GL_CLAMP borders on ati cards */
+       if( skyFixHack && si->skyParmsImageBase[ 0 ] != '\0' )
+       {
+               //%     Sys_FPrintf( SYS_VRB, "Enabling sky hack for shader %s using env %s\n", si->shader, si->skyParmsImageBase );
+               sprintf( tempShader, "%s_lf", si->skyParmsImageBase );
+               DrawSurfaceForShader( tempShader );
+               sprintf( tempShader, "%s_rt", si->skyParmsImageBase );
+               DrawSurfaceForShader( tempShader );
+               sprintf( tempShader, "%s_ft", si->skyParmsImageBase );
+               DrawSurfaceForShader( tempShader );
+               sprintf( tempShader, "%s_bk", si->skyParmsImageBase );
+               DrawSurfaceForShader( tempShader );
+               sprintf( tempShader, "%s_up", si->skyParmsImageBase );
+               DrawSurfaceForShader( tempShader );
+               sprintf( tempShader, "%s_dn", si->skyParmsImageBase );
+               DrawSurfaceForShader( tempShader );
+       }
+       
+       /* ydnar: gs mods */
+       ds = AllocDrawSurface( SURFACE_FACE );
+       ds->entityNum = b->entityNum;
+       ds->castShadows = b->castShadows;
+       ds->recvShadows = b->recvShadows;
+       
+       ds->planar = qtrue;
+       ds->planeNum = s->planenum;
+       VectorCopy( mapplanes[ s->planenum ].normal, ds->lightmapVecs[ 2 ] );
+       
+       ds->shaderInfo = si;
+       ds->mapBrush = b;
+       ds->sideRef = AllocSideRef( s, NULL );
+       ds->fogNum = -1;
+       ds->lightmapScale = b->lightmapScale;
+       ds->numVerts = w->numpoints;
+       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
+       memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
+       
+       /* compute s/t coordinates from brush primitive texture matrix (compute axis base) */
+       ComputeAxisBase( mapplanes[ s->planenum ].normal, texX, texY );
+       
+       /* create the vertexes */
+       for( j = 0; j < w->numpoints; j++ )
+       {
+               /* get the drawvert */
+               dv = ds->verts + j;
+               
+               /* copy xyz and do potential z offset */
+               VectorCopy( w->p[ j ], dv->xyz );
+               if( indexed )
+                       dv->xyz[ 2 ] += offsets[ j ];
+               
+               /* round the xyz to a given precision and translate by origin */
+               for( i = 0 ; i < 3 ; i++ )
+                       dv->xyz[ i ] = SNAP_INT_TO_FLOAT * floor( dv->xyz[ i ] * SNAP_FLOAT_TO_INT + 0.5f );
+               VectorAdd( dv->xyz, e->origin, vTranslated );
+               
+               /* ydnar: tek-fu celshading support for flat shaded shit */
+               if( flat )
+               {
+                       dv->st[ 0 ] = si->stFlat[ 0 ];
+                       dv->st[ 1 ] = si->stFlat[ 1 ];
+               }
+               
+               /* ydnar: gs mods: added support for explicit shader texcoord generation */
+               else if( si->tcGen )
+               {
+                       dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], vTranslated );
+                       dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], vTranslated );
+               }
+               
+               /* old quake-style texturing */
+               else if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
+               {
+                       /* nearest-axial projection */
+                       dv->st[ 0 ] = s->vecs[ 0 ][ 3 ] + DotProduct( s->vecs[ 0 ], vTranslated );
+                       dv->st[ 1 ] = s->vecs[ 1 ][ 3 ] + DotProduct( s->vecs[ 1 ], vTranslated );
+                       dv->st[ 0 ] /= si->shaderWidth;
+                       dv->st[ 1 ] /= si->shaderHeight;
+               }
+               
+               /* brush primitive texturing */
+               else
+               {
+                       /* calculate texture s/t from brush primitive texture matrix */
+                       x = DotProduct( vTranslated, texX );
+                       y = DotProduct( vTranslated, texY );
+                       dv->st[ 0 ] = s->texMat[ 0 ][ 0 ] * x + s->texMat[ 0 ][ 1 ] * y + s->texMat[ 0 ][ 2 ];
+                       dv->st[ 1 ] = s->texMat[ 1 ][ 0 ] * x + s->texMat[ 1 ][ 1 ] * y + s->texMat[ 1 ][ 2 ];
+               }
+               
+               /* copy normal */
+               VectorCopy( mapplanes[ s->planenum ].normal, dv->normal );
+               
+               /* ydnar: set color */
+               for( k = 0; k < MAX_LIGHTMAPS; k++ )
+               {
+                       dv->color[ k ][ 0 ] = 255;
+                       dv->color[ k ][ 1 ] = 255;
+                       dv->color[ k ][ 2 ] = 255;
+                       
+                       /* ydnar: gs mods: handle indexed shader blending */
+                       dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ j ] : 255);
+               }
+       }
+       
+       /* set cel shader */
+       ds->celShader = b->celShader;
+       
+       /* finish surface */
+       FinishSurface( ds );
+       
+       /* ydnar: gs mods: moved st biasing elsewhere */
+       return ds;
+}
+
+
+
+/*
+DrawSurfaceForMesh()
+moved here from patch.c
+*/
+
+#define YDNAR_NORMAL_EPSILON 0.50f
+
+qboolean VectorCompareExt( vec3_t n1, vec3_t n2, float epsilon )
+{
+       int             i;
+       
+       
+       /* test */
+       for( i= 0; i < 3; i++ )
+               if( fabs( n1[ i ] - n2[ i ]) > epsilon )
+                       return qfalse;
+       return qtrue;
+}
+
+mapDrawSurface_t *DrawSurfaceForMesh( entity_t *e, parseMesh_t *p, mesh_t *mesh )
+{
+       int                                     i, k, numVerts;
+       vec4_t                          plane;
+       qboolean                        planar;
+       float                           dist;
+       mapDrawSurface_t        *ds;
+       shaderInfo_t            *si, *parent;
+       bspDrawVert_t           *dv;
+       vec3_t                          vTranslated;
+       mesh_t                          *copy;
+       qboolean                        indexed;
+       byte                            shaderIndexes[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];
+       float                           offsets[ MAX_EXPANDED_AXIS * MAX_EXPANDED_AXIS ];
+       
+       
+       /* get mesh and shader shader */
+       if( mesh == NULL )
+               mesh = &p->mesh;
+       si = p->shaderInfo;
+       if( mesh == NULL || si == NULL )
+               return NULL;
+       
+       /* get vertex count */
+       numVerts = mesh->width * mesh->height;
+       
+       /* to make valid normals for patches with degenerate edges,
+          we need to make a copy of the mesh and put the aproximating
+          points onto the curve */
+       
+       /* create a copy of the mesh */
+       copy = CopyMesh( mesh );
+       
+       /* store off the original (potentially bad) normals */
+       MakeMeshNormals( *copy );
+       for( i = 0; i < numVerts; i++ )
+               VectorCopy( copy->verts[ i ].normal, mesh->verts[ i ].normal );
+       
+       /* put the mesh on the curve */
+       PutMeshOnCurve( *copy );
+
+       /* find new normals (to take into account degenerate/flipped edges */
+       MakeMeshNormals( *copy );
+       for( i = 0; i < numVerts; i++ )
+       {
+               /* ydnar: only copy normals that are significantly different from the originals */
+               if( DotProduct( copy->verts[ i ].normal, mesh->verts[ i ].normal ) < 0.75f )
+                       VectorCopy( copy->verts[ i ].normal, mesh->verts[ i ].normal );
+       }
+       
+       /* free the old mesh */
+       FreeMesh( copy );
+       
+       /* ydnar: gs mods: check for indexed shader */
+       if( si->indexed && p->im != NULL )
+       {
+               /* indexed */
+               indexed = qtrue;
+
+               /* get shader indexes for each point */
+               for( i = 0; i < numVerts; i++ )
+               {
+                       shaderIndexes[ i ] = GetShaderIndexForPoint( p->im, p->eMins, p->eMaxs, mesh->verts[ i ].xyz );
+                       offsets[ i ] = p->im->offsets[ shaderIndexes[ i ] ];
+               }
+               
+               /* get matching shader and set alpha */
+               parent = si;
+               si = GetIndexedShader( parent, p->im, numVerts, shaderIndexes );
+       }
+       else
+               indexed = qfalse;
+       
+       
+       /* ydnar: gs mods */
+       ds = AllocDrawSurface( SURFACE_PATCH );
+       ds->entityNum = p->entityNum;
+       ds->castShadows = p->castShadows;
+       ds->recvShadows = p->recvShadows;
+       
+       ds->shaderInfo = si;
+       ds->mapMesh = p;
+       ds->lightmapScale = p->lightmapScale;   /* ydnar */
+       ds->patchWidth = mesh->width;
+       ds->patchHeight = mesh->height;
+       ds->numVerts = ds->patchWidth * ds->patchHeight;
+       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
+       memcpy( ds->verts, mesh->verts, ds->numVerts * sizeof( *ds->verts ) );
+       
+       ds->fogNum = -1;
+       ds->planeNum = -1;
+       
+       ds->longestCurve = p->longestCurve;
+       ds->maxIterations = p->maxIterations;
+       
+       /* construct a plane from the first vert */
+       VectorCopy( mesh->verts[ 0 ].normal, plane );
+       plane[ 3 ] = DotProduct( mesh->verts[ 0 ].xyz, plane );
+       planar = qtrue;
+       
+       /* spew forth errors */
+       if( VectorLength( plane ) < 0.001f )
+               Sys_Printf( "BOGUS " );
+       
+       /* test each vert */
+       for( i = 1; i < ds->numVerts && planar; i++ )
+       {
+               /* normal test */
+               if( VectorCompare( plane, mesh->verts[ i ].normal ) == qfalse )
+                       planar = qfalse;
+               
+               /* point-plane test */
+               dist = DotProduct( mesh->verts[ i ].xyz, plane ) - plane[ 3 ];
+               if( fabs( dist ) > EQUAL_EPSILON )
+                       planar = qfalse;
+       }
+       
+       /* add a map plane */
+       if( planar )
+       {
+               /* make a map plane */
+               ds->planeNum = FindFloatPlane( plane, plane[ 3 ], 1, &mesh->verts[ 0 ].xyz );
+               VectorCopy( plane, ds->lightmapVecs[ 2 ] );
+               
+               /* push this normal to all verts (ydnar 2003-02-14: bad idea, small patches get screwed up) */
+               for( i = 0; i < ds->numVerts; i++ )
+                       VectorCopy( plane, ds->verts[ i ].normal );
+       }
+       
+       /* walk the verts to do special stuff */
+       for( i = 0; i < ds->numVerts; i++ )
+       {
+               /* get the drawvert */
+               dv = &ds->verts[ i ];
+               
+               /* ydnar: tek-fu celshading support for flat shaded shit */
+               if( flat )
+               {
+                       dv->st[ 0 ] = si->stFlat[ 0 ];
+                       dv->st[ 1 ] = si->stFlat[ 1 ];
+               }
+               
+               /* ydnar: gs mods: added support for explicit shader texcoord generation */
+               else if( si->tcGen )
+               {
+                       /* translate by origin and project the texture */
+                       VectorAdd( dv->xyz, e->origin, vTranslated );
+                       dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], vTranslated );
+                       dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], vTranslated );
+               }
+               
+               /* ydnar: set color */
+               for( k = 0; k < MAX_LIGHTMAPS; k++ )
+               {
+                       dv->color[ k ][ 0 ] = 255;
+                       dv->color[ k ][ 1 ] = 255;
+                       dv->color[ k ][ 2 ] = 255;
+                       
+                       /* ydnar: gs mods: handle indexed shader blending */
+                       dv->color[ k ][ 3 ] = (indexed ? shaderIndexes[ i ] : 255);
+               }
+               
+               /* ydnar: offset */
+               if( indexed )
+                       dv->xyz[ 2 ] += offsets[ i ];
+       }
+       
+       /* set cel shader */
+       ds->celShader = p->celShader;
+       
+       /* finish surface */
+       FinishSurface( ds );
+       
+       /* return the drawsurface */
+       return ds;
+}
+
+
+
+/*
+DrawSurfaceForFlare() - ydnar
+creates a flare draw surface
+*/
+
+mapDrawSurface_t *DrawSurfaceForFlare( int entNum, vec3_t origin, vec3_t normal, vec3_t color, char *flareShader, int lightStyle )
+{
+       mapDrawSurface_t        *ds;
+       
+       
+       /* emit flares? */
+       if( emitFlares == qfalse )
+               return NULL;
+       
+       /* allocate drawsurface */
+       ds = AllocDrawSurface( SURFACE_FLARE );
+       ds->entityNum = entNum;
+       
+       /* set it up */
+       if( flareShader != NULL && flareShader[ 0 ] != '\0' )
+               ds->shaderInfo = ShaderInfoForShader( flareShader );
+       else
+               ds->shaderInfo = ShaderInfoForShader( game->flareShader );
+       if( origin != NULL )
+               VectorCopy( origin, ds->lightmapOrigin );
+       if( normal != NULL )
+               VectorCopy( normal, ds->lightmapVecs[ 2 ] );
+       if( color != NULL )
+               VectorCopy( color, ds->lightmapVecs[ 0 ] );
+       
+       /* store light style */
+       ds->lightStyle = lightStyle;
+       if( ds->lightStyle < 0 || ds->lightStyle >= LS_NONE )
+               ds->lightStyle = LS_NORMAL;
+       
+       /* fixme: fog */
+       
+       /* return to sender */
+       return ds;
+}
+
+
+
+/*
+DrawSurfaceForShader() - ydnar
+creates a bogus surface to forcing the game to load a shader
+*/
+
+mapDrawSurface_t *DrawSurfaceForShader( char *shader )
+{
+       int                                     i;
+       shaderInfo_t            *si;
+       mapDrawSurface_t        *ds;
+       
+       
+       /* get shader */
+       si = ShaderInfoForShader( shader );
+
+       /* find existing surface */
+       for( i = 0; i < numMapDrawSurfs; i++ )
+       {
+               /* get surface */
+               ds = &mapDrawSurfs[ i ];
+               
+               /* check it */
+               if( ds->shaderInfo == si )
+                       return ds;
+       }
+       
+       /* create a new surface */
+       ds = AllocDrawSurface( SURFACE_SHADER );
+       ds->entityNum = 0;
+       ds->shaderInfo = ShaderInfoForShader( shader );
+       
+       /* return to sender */
+       return ds;
+}
+
+
+
+/*
+AddSurfaceFlare() - ydnar
+creates flares (coronas) centered on surfaces
+*/
+
+static void AddSurfaceFlare( mapDrawSurface_t *ds, vec3_t entityOrigin )
+{
+       vec3_t                          origin;
+       int                                     i;
+       
+       
+       /* find centroid */
+       VectorClear( origin );
+       for ( i = 0; i < ds->numVerts; i++ )
+               VectorAdd( origin, ds->verts[ i ].xyz, origin );
+       VectorScale( origin, (1.0f / ds->numVerts), origin );
+       if( entityOrigin != NULL )
+               VectorAdd( origin, entityOrigin, origin );
+       
+       /* push origin off surface a bit */
+       VectorMA( origin, 2.0f,  ds->lightmapVecs[ 2 ], origin );
+       
+       /* create the drawsurface */
+       DrawSurfaceForFlare( ds->entityNum, origin, ds->lightmapVecs[ 2 ], ds->shaderInfo->color, ds->shaderInfo->flareShader, ds->shaderInfo->lightStyle );
+}
+
+
+
+/*
+SubdivideFace()
+subdivides a face surface until it is smaller than the specified size (subdivisions)
+*/
+
+static void SubdivideFace( entity_t *e, brush_t *brush, side_t *side, winding_t *w, int fogNum, float subdivisions )
+{
+       int                                     i;
+       int                                     axis;
+       vec3_t                          bounds[ 2 ];
+       const float                     epsilon = 0.1;
+       int                                     subFloor, subCeil;
+       winding_t                       *frontWinding, *backWinding;
+       mapDrawSurface_t        *ds;
+       
+       
+       /* dummy check */
+       if( w == NULL )
+               return;
+       if( w->numpoints < 3 )
+               Error( "SubdivideFaceSurface: Bad w->numpoints" );
+       
+       /* determine surface bounds */
+       ClearBounds( bounds[ 0 ], bounds[ 1 ] );
+       for( i = 0; i < w->numpoints; i++ )
+               AddPointToBounds( w->p[ i ], bounds[ 0 ], bounds[ 1 ] );
+       
+       /* split the face */
+       for( axis = 0; axis < 3; axis++ )
+       {
+               vec3_t                  planePoint = { 0, 0, 0 };
+               vec3_t                  planeNormal = { 0, 0, 0 };
+               float                   d;
+               
+               
+               /* create an axial clipping plane */
+               subFloor = floor( bounds[ 0 ][ axis ] / subdivisions) * subdivisions;
+               subCeil = ceil( bounds[ 1 ][ axis ] / subdivisions) * subdivisions;
+               planePoint[ axis ] = subFloor + subdivisions;
+               planeNormal[ axis ] = -1;
+               d = DotProduct( planePoint, planeNormal );
+
+               /* subdivide if necessary */
+               if( (subCeil - subFloor) > subdivisions )
+               {
+                       /* clip the winding */
+                       ClipWindingEpsilon( w, planeNormal, d, epsilon, &frontWinding, &backWinding );
+
+                       /* the clip may not produce two polygons if it was epsilon close */
+                       if( frontWinding == NULL )
+                               w = backWinding;
+                       else if( backWinding == NULL )
+                               w = frontWinding;
+                       else
+                       {
+                               SubdivideFace( e, brush, side, frontWinding, fogNum, subdivisions );
+                               SubdivideFace( e, brush, side, backWinding, fogNum, subdivisions );
+                               return;
+                       }
+               }
+       }
+       
+       /* create a face surface */
+       ds = DrawSurfaceForSide( e, brush, side, w );
+       
+       /* set correct fog num */
+       ds->fogNum = fogNum;
+}
+
+
+
+/*
+SubdivideFaceSurfaces()
+chop up brush face surfaces that have subdivision attributes
+ydnar: and subdivide surfaces that exceed specified texture coordinate range
+*/
+
+void SubdivideFaceSurfaces( entity_t *e, tree_t *tree )
+{
+       int                                     i, j, numBaseDrawSurfs, fogNum;
+       mapDrawSurface_t        *ds;
+       brush_t                         *brush;
+       side_t                          *side;
+       shaderInfo_t            *si;
+       winding_t                       *w;
+       float                           range, size, subdivisions, s2;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- SubdivideFaceSurfaces ---\n" );
+       
+       /* walk the list of surfaces */
+       numBaseDrawSurfs = numMapDrawSurfs;
+       for( i = e->firstDrawSurf; i < numBaseDrawSurfs; i++ )
+       {
+               /* get surface */
+               ds = &mapDrawSurfs[ i ];
+
+               /* only subdivide brush sides */
+               if( ds->type != SURFACE_FACE || ds->mapBrush == NULL || ds->sideRef == NULL || ds->sideRef->side == NULL )
+                       continue;
+               
+               /* get bits */
+               brush = ds->mapBrush;
+               side = ds->sideRef->side;
+               
+               /* check subdivision for shader */
+               si = side->shaderInfo;
+               if( si == NULL )
+                       continue;
+               
+               /* ydnar: don't subdivide sky surfaces */
+               if( si->compileFlags & C_SKY )
+                       continue;
+               
+               /* do texture coordinate range check */
+               ClassifySurfaces( 1, ds );
+               if( CalcSurfaceTextureRange( ds ) == qfalse )
+               {
+                       /* calculate subdivisions texture range (this code is shit) */
+                       range = (ds->texRange[ 0 ] > ds->texRange[ 1 ] ? ds->texRange[ 0 ] : ds->texRange[ 1 ]);
+                       size = ds->maxs[ 0 ] - ds->mins[ 0 ];
+                       for( j = 1; j < 3; j++ )
+                               if( (ds->maxs[ j ] - ds->mins[ j ]) > size )
+                                       size = ds->maxs[ j ] - ds->mins[ j ];
+                       subdivisions = (size / range) * texRange;
+                       subdivisions = ceil( subdivisions / 2 ) * 2;
+                       for( j = 1; j < 8; j++ )
+                       {
+                               s2 = ceil( (float) texRange / j );
+                               if( fabs( subdivisions - s2 ) <= 4.0 )
+                               {
+                                       subdivisions = s2;
+                                       break;
+                               }
+                       }
+               }
+               else
+                       subdivisions = si->subdivisions;
+               
+               /* get subdivisions from shader */
+               if(     si->subdivisions > 0 && si->subdivisions < subdivisions )
+                       subdivisions = si->subdivisions;
+               if( subdivisions < 1.0f )
+                       continue;
+               
+               /* preserve fog num */
+               fogNum = ds->fogNum;
+               
+               /* make a winding and free the surface */
+               w = WindingFromDrawSurf( ds );
+               ClearSurface( ds );
+               
+               /* subdivide it */
+               SubdivideFace( e, brush, side, w, fogNum, subdivisions );
+       }
+}
+
+
+
+/*
+====================
+ClipSideIntoTree_r
+
+Adds non-opaque leaf fragments to the convex hull
+====================
+*/
+
+void ClipSideIntoTree_r( winding_t *w, side_t *side, node_t *node )
+{
+       plane_t                 *plane;
+       winding_t               *front, *back;
+
+       if ( !w ) {
+               return;
+       }
+
+       if ( node->planenum != PLANENUM_LEAF ) {
+               if ( side->planenum == node->planenum ) {
+                       ClipSideIntoTree_r( w, side, node->children[0] );
+                       return;
+               }
+               if ( side->planenum == ( node->planenum ^ 1) ) {
+                       ClipSideIntoTree_r( w, side, node->children[1] );
+                       return;
+               }
+
+               plane = &mapplanes[ node->planenum ];
+               ClipWindingEpsilon ( w, plane->normal, plane->dist,
+                               ON_EPSILON, &front, &back );
+               FreeWinding( w );
+
+               ClipSideIntoTree_r( front, side, node->children[0] );
+               ClipSideIntoTree_r( back, side, node->children[1] );
+
+               return;
+       }
+
+       // if opaque leaf, don't add
+       if ( !node->opaque ) {
+               AddWindingToConvexHull( w, &side->visibleHull, mapplanes[ side->planenum ].normal );
+       }
+
+       FreeWinding( w );
+       return;
+}
+
+
+
+
+
+static int g_numHiddenFaces, g_numCoinFaces;
+
+
+
+/*
+CullVectorCompare() - ydnar
+compares two vectors with an epsilon
+*/
+
+#define CULL_EPSILON 0.1f
+
+qboolean CullVectorCompare( const vec3_t v1, const vec3_t v2 )
+{
+       int             i;
+       
+       
+       for( i = 0; i < 3; i++ )
+               if( fabs( v1[ i ] - v2[ i ] ) > CULL_EPSILON )
+                       return qfalse;
+       return qtrue;
+}
+
+
+
+/*
+SideInBrush() - ydnar
+determines if a brushside lies inside another brush
+*/
+
+qboolean SideInBrush( side_t *side, brush_t *b )
+{
+       int                     i, s;
+       plane_t         *plane;
+       
+       
+       /* ignore sides w/o windings or shaders */
+       if( side->winding == NULL || side->shaderInfo == NULL )
+               return qtrue;
+
+       /* ignore culled sides and translucent brushes */
+       if( side->culled == qtrue || (b->compileFlags & C_TRANSLUCENT) )
+               return qfalse;
+
+       /* side iterator */
+       for( i = 0; i < b->numsides; i++ )
+       {
+               /* fail if any sides are caulk */
+               if( b->sides[ i ].compileFlags & C_NODRAW )
+                       return qfalse;
+
+               /* check if side's winding is on or behind the plane */
+               plane = &mapplanes[ b->sides[ i ].planenum ];
+               s = WindingOnPlaneSide( side->winding, plane->normal, plane->dist );
+               if( s == SIDE_FRONT || s == SIDE_CROSS )
+                       return qfalse;
+       }
+       
+       /* don't cull autosprite or polygonoffset surfaces */
+       if( side->shaderInfo )
+       {
+               if( side->shaderInfo->autosprite || side->shaderInfo->polygonOffset )
+                       return qfalse;
+       }
+       
+       /* inside */
+       side->culled = qtrue;
+       g_numHiddenFaces++;
+       return qtrue;
+}
+
+
+/*
+CullSides() - ydnar
+culls obscured or buried brushsides from the map
+*/
+
+void CullSides( entity_t *e )
+{
+       int                     numPoints;
+       int                     i, j, k, l, first, second, dir;
+       winding_t       *w1, *w2;
+       brush_t *b1, *b2;
+       side_t          *side1, *side2;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- CullSides ---\n" );
+       
+       g_numHiddenFaces = 0;
+       g_numCoinFaces = 0;
+       
+       /* brush interator 1 */
+       for( b1 = e->brushes; b1; b1 = b1->next )
+       {
+               /* sides check */
+               if( b1->numsides < 1 )
+                       continue;
+
+               /* brush iterator 2 */
+               for( b2 = b1->next; b2; b2 = b2->next )
+               {
+                       /* sides check */
+                       if( b2->numsides < 1 )
+                               continue;
+                       
+                       /* original check */
+                       if( b1->original == b2->original && b1->original != NULL )
+                               continue;
+                       
+                       /* bbox check */
+                       j = 0;
+                       for( i = 0; i < 3; i++ )
+                               if( b1->mins[ i ] > b2->maxs[ i ] || b1->maxs[ i ] < b2->mins[ i ] )
+                                       j++;
+                       if( j )
+                               continue;
+
+                       /* cull inside sides */
+                       for( i = 0; i < b1->numsides; i++ )
+                               SideInBrush( &b1->sides[ i ], b2 );
+                       for( i = 0; i < b2->numsides; i++ )
+                               SideInBrush( &b2->sides[ i ], b1 );
+                       
+                       /* side iterator 1 */
+                       for( i = 0; i < b1->numsides; i++ )
+                       {
+                               /* winding check */
+                               side1 = &b1->sides[ i ];
+                               w1 = side1->winding;
+                               if( w1 == NULL )
+                                       continue;
+                               numPoints = w1->numpoints;
+                               if( side1->shaderInfo == NULL )
+                                       continue;
+                               
+                               /* side iterator 2 */
+                               for( j = 0; j < b2->numsides; j++ )
+                               {
+                                       /* winding check */
+                                       side2 = &b2->sides[ j ];
+                                       w2 = side2->winding;
+                                       if( w2 == NULL )
+                                               continue;
+                                       if( side2->shaderInfo == NULL )
+                                               continue;
+                                       if( w1->numpoints != w2->numpoints )
+                                               continue;
+                                       if( side1->culled == qtrue && side2->culled == qtrue )
+                                               continue;
+                                       
+                                       /* compare planes */
+                                       if( (side1->planenum & ~0x00000001) != (side2->planenum & ~0x00000001) )
+                                               continue;
+                                       
+                                       /* get autosprite and polygonoffset status */
+                                       if( side1->shaderInfo &&
+                                               (side1->shaderInfo->autosprite || side1->shaderInfo->polygonOffset) )
+                                               continue;
+                                       if( side2->shaderInfo &&
+                                               (side2->shaderInfo->autosprite || side2->shaderInfo->polygonOffset) )
+                                               continue;
+                                       
+                                       /* find first common point */
+                                       first = -1;
+                                       for( k = 0; k < numPoints; k++ )
+                                       {
+                                               if( VectorCompare( w1->p[ 0 ], w2->p[ k ] ) )
+                                               {
+                                                       first = k;
+                                                       k = numPoints;
+                                               }
+                                       }
+                                       if( first == -1 )
+                                               continue;
+                                       
+                                       /* find second common point (regardless of winding order) */
+                                       second = -1;
+                                       dir = 0;
+                                       if( (first + 1) < numPoints )
+                                               second = first + 1;
+                                       else
+                                               second = 0;
+                                       if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )
+                                               dir = 1;
+                                       else
+                                       {
+                                               if( first > 0 )
+                                                       second = first - 1;
+                                               else
+                                                       second = numPoints - 1;
+                                               if( CullVectorCompare( w1->p[ 1 ], w2->p[ second ] ) )
+                                                       dir = -1;
+                                       }
+                                       if( dir == 0 )
+                                               continue;
+                                       
+                                       /* compare the rest of the points */
+                                       l = first;
+                                       for( k = 0; k < numPoints; k++ )
+                                       {
+                                               if( !CullVectorCompare( w1->p[ k ], w2->p[ l ] ) )
+                                                       k = 100000;
+                                               
+                                               l += dir;
+                                               if( l < 0 )
+                                                       l = numPoints - 1;
+                                               else if( l >= numPoints )
+                                                       l = 0;
+                                       }
+                                       if( k >= 100000 )
+                                               continue;
+                                       
+                                       /* cull face 1 */
+                                       if( !side2->culled && !(side2->compileFlags & C_TRANSLUCENT) && !(side2->compileFlags & C_NODRAW) )
+                                       {
+                                               side1->culled = qtrue;
+                                               g_numCoinFaces++;
+                                       }
+                                       
+                                       if( side1->planenum == side2->planenum && side1->culled == qtrue )
+                                               continue;
+                                       
+                                       /* cull face 2 */
+                                       if( !side1->culled && !(side1->compileFlags & C_TRANSLUCENT) && !(side1->compileFlags & C_NODRAW) )
+                                       {
+                                               side2->culled = qtrue;
+                                               g_numCoinFaces++;
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       /* emit some stats */
+       Sys_FPrintf( SYS_VRB, "%9d hidden faces culled\n", g_numHiddenFaces );
+       Sys_FPrintf( SYS_VRB, "%9d coincident faces culled\n", g_numCoinFaces );
+}
+
+
+
+
+/*
+ClipSidesIntoTree()
+
+creates side->visibleHull for all visible sides
+
+the drawsurf for a side will consist of the convex hull of
+all points in non-opaque clusters, which allows overlaps
+to be trimmed off automatically.
+*/
+
+void ClipSidesIntoTree( entity_t *e, tree_t *tree )
+{
+       brush_t         *b;
+       int                             i;
+       winding_t               *w;
+       side_t                  *side, *newSide;
+       shaderInfo_t    *si;
+  
+       
+       /* ydnar: cull brush sides */
+       CullSides( e );
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- ClipSidesIntoTree ---\n" );
+       
+       /* walk the brush list */
+       for( b = e->brushes; b; b = b->next )
+       {
+               /* walk the brush sides */
+               for( i = 0; i < b->numsides; i++ )
+               {
+                       /* get side */
+                       side = &b->sides[ i ];
+                       if( side->winding == NULL )
+                               continue;
+                       
+                       /* copy the winding */
+                       w = CopyWinding( side->winding );
+                       side->visibleHull = NULL;
+                       ClipSideIntoTree_r( w, side, tree->headnode );
+                       
+                       /* anything left? */
+                       w = side->visibleHull;
+                       if( w == NULL )
+                               continue;
+                       
+                       /* shader? */
+                       si = side->shaderInfo;
+                       if( si == NULL )
+                               continue;
+                       
+                       /* don't create faces for non-visible sides */
+                       /* ydnar: except indexed shaders, like common/terrain and nodraw fog surfaces */
+                       if( (si->compileFlags & C_NODRAW) && si->indexed == qfalse && !(si->compileFlags & C_FOG) )
+                               continue;
+                       
+                       /* always use the original winding for autosprites and noclip faces */
+                       if( si->autosprite || si->noClip )
+                               w = side->winding;
+                       
+                       /* save this winding as a visible surface */
+                       DrawSurfaceForSide( e, b, side, w );
+
+                       /* make a back side for fog */
+                       if( !(si->compileFlags & C_FOG) )
+                               continue;
+                       
+                       /* duplicate the up-facing side */
+                       w = ReverseWinding( w );
+                       newSide = safe_malloc( sizeof( *side ) );
+                       *newSide = *side;
+                       newSide->visibleHull = w;
+                       newSide->planenum ^= 1;
+                       
+                       /* save this winding as a visible surface */
+                       DrawSurfaceForSide( e, b, newSide, w );
+               }
+       }
+}
+
+
+
+/*
+
+this section deals with filtering drawsurfaces into the bsp tree,
+adding references to each leaf a surface touches
+
+*/
+
+/*
+AddReferenceToLeaf() - ydnar
+adds a reference to surface ds in the bsp leaf node
+*/
+
+int AddReferenceToLeaf( mapDrawSurface_t *ds, node_t *node )
+{
+       drawSurfRef_t   *dsr;
+       
+       
+       /* dummy check */
+       if( node->planenum != PLANENUM_LEAF || node->opaque )
+               return 0;
+       
+       /* try to find an existing reference */
+       for( dsr = node->drawSurfReferences; dsr; dsr = dsr->nextRef )
+       {
+               if( dsr->outputNum == numBSPDrawSurfaces )
+                       return 0;
+       }
+       
+       /* add a new reference */
+       dsr = safe_malloc( sizeof( *dsr ) );
+       dsr->outputNum = numBSPDrawSurfaces;
+       dsr->nextRef = node->drawSurfReferences;
+       node->drawSurfReferences = dsr;
+       
+       /* ydnar: sky/skybox surfaces */
+       if( node->skybox )
+               ds->skybox = qtrue;
+       if( ds->shaderInfo->compileFlags & C_SKY )
+               node->sky = qtrue;
+       
+       /* return */
+       return 1;
+}
+
+
+
+/*
+AddReferenceToTree_r() - ydnar
+adds a reference to the specified drawsurface to every leaf in the tree
+*/
+
+int AddReferenceToTree_r( mapDrawSurface_t *ds, node_t *node, qboolean skybox )
+{
+       int             i, refs = 0;
+       
+       
+       /* dummy check */
+       if( node == NULL )
+               return 0;
+       
+       /* is this a decision node? */
+       if( node->planenum != PLANENUM_LEAF )
+       {
+               /* add to child nodes and return */
+               refs += AddReferenceToTree_r( ds, node->children[ 0 ], skybox );
+               refs += AddReferenceToTree_r( ds, node->children[ 1 ], skybox );
+               return refs;
+       }
+       
+       /* ydnar */
+       if( skybox )
+       {
+               /* skybox surfaces only get added to sky leaves */
+               if( !node->sky )
+                       return 0;
+               
+               /* increase the leaf bounds */
+               for( i = 0; i < ds->numVerts; i++ )
+                       AddPointToBounds( ds->verts[ i ].xyz, node->mins, node->maxs );
+       }
+       
+       /* add a reference */
+       return AddReferenceToLeaf( ds, node );
+}
+
+
+
+/*
+FilterPointIntoTree_r() - ydnar
+filters a single point from a surface into the tree
+*/
+
+int FilterPointIntoTree_r( vec3_t point, mapDrawSurface_t *ds, node_t *node )
+{
+       float                   d;
+       plane_t                 *plane;
+       int                             refs = 0;
+       
+       
+       /* is this a decision node? */
+       if( node->planenum != PLANENUM_LEAF )
+       {
+               /* classify the point in relation to the plane */
+               plane = &mapplanes[ node->planenum ];
+               d = DotProduct( point, plane->normal ) - plane->dist;
+               
+               /* filter by this plane */
+               refs = 0;
+               if( d >= -ON_EPSILON )
+                       refs += FilterPointIntoTree_r( point, ds, node->children[ 0 ] );
+               if( d <= ON_EPSILON )
+                       refs += FilterPointIntoTree_r( point, ds, node->children[ 1 ] );
+               
+               /* return */
+               return refs;
+       }
+       
+       /* add a reference */
+       return AddReferenceToLeaf( ds, node );
+}
+
+
+
+/*
+FilterWindingIntoTree_r() - ydnar
+filters a winding from a drawsurface into the tree
+*/
+
+int FilterWindingIntoTree_r( winding_t *w, mapDrawSurface_t *ds, node_t *node )
+{
+       int                             i, refs = 0;
+       plane_t                 *p1, *p2;
+       vec4_t                  plane1, plane2, reverse;
+       winding_t               *fat, *front, *back;
+       shaderInfo_t    *si;
+       
+       
+       /* get shaderinfo */
+       si = ds->shaderInfo;
+       
+       /* ydnar: is this the head node? */
+       if( node->parent == NULL && si != NULL &&
+               (si->mins[ 0 ] != 0.0f || si->maxs[ 0 ] != 0.0f ||
+               si->mins[ 1 ] != 0.0f || si->maxs[ 1 ] != 0.0f ||
+               si->mins[ 2 ] != 0.0f || si->maxs[ 2 ] != 0.0f) )
+       {
+               /* 'fatten' the winding by the shader mins/maxs (parsed from vertexDeform move) */
+               /* note this winding is completely invalid (concave, nonplanar, etc) */
+               fat = AllocWinding( w->numpoints * 3 );
+               fat->numpoints = w->numpoints * 3;
+               for( i = 0; i < w->numpoints; i++ )
+               {
+                       VectorCopy( w->p[ i ], fat->p[ i ] );
+                       VectorAdd( w->p[ i ], si->mins, fat->p[ i * 2 ] );
+                       VectorAdd( w->p[ i ], si->maxs, fat->p[ i * 3 ] );
+               }
+               
+               FreeWinding( w );
+               w = fat;
+       }
+       
+       /* is this a decision node? */
+       if( node->planenum != PLANENUM_LEAF )
+       {       
+               /* get node plane */
+               p1 = &mapplanes[ node->planenum ];
+               VectorCopy( p1->normal, plane1 );
+               plane1[ 3 ] = p1->dist;
+               
+               /* check if surface is planar */
+               if( ds->planeNum >= 0 )
+               {
+                       /* get surface plane */
+                       p2 = &mapplanes[ ds->planeNum ];
+                       VectorCopy( p2->normal, plane2 );
+                       plane2[ 3 ] = p2->dist;
+                       
+                       #if 1
+                               /* invert surface plane */
+                               VectorSubtract( vec3_origin, plane2, reverse );
+                               reverse[ 3 ] = -plane2[ 3 ];
+                               
+                               /* compare planes */
+                               if( DotProduct( plane1, plane2 ) > 0.999f && fabs( plane1[ 3 ] - plane2[ 3 ] ) < 0.001f )
+                                       return FilterWindingIntoTree_r( w, ds, node->children[ 0 ] );
+                               if( DotProduct( plane1, reverse ) > 0.999f && fabs( plane1[ 3 ] - reverse[ 3 ] ) < 0.001f )
+                                       return FilterWindingIntoTree_r( w, ds, node->children[ 1 ] );
+                       #else
+                               /* the drawsurf might have an associated plane, if so, force a filter here */
+                               if( ds->planeNum == node->planenum )
+                                       return FilterWindingIntoTree_r( w, ds, node->children[ 0 ] );
+                               if( ds->planeNum == (node->planenum ^ 1) )
+                                       return FilterWindingIntoTree_r( w, ds, node->children[ 1 ] );
+                       #endif
+               }
+               
+               /* clip the winding by this plane */
+               ClipWindingEpsilon( w, plane1, plane1[ 3 ], ON_EPSILON, &front, &back );
+               
+               /* filter by this plane */
+               refs = 0;
+               if( front != NULL )
+                       refs += FilterWindingIntoTree_r( front, ds, node->children[ 0 ] );
+               if( back != NULL )
+                       refs += FilterWindingIntoTree_r( back, ds, node->children[ 1 ] );
+               FreeWinding( w );
+               
+               /* return */
+               return refs;
+       }
+       
+       /* add a reference */
+       return AddReferenceToLeaf( ds, node );
+}
+
+
+
+/*
+FilterFaceIntoTree()
+filters a planar winding face drawsurface into the bsp tree
+*/
+
+int    FilterFaceIntoTree( mapDrawSurface_t *ds, tree_t *tree )
+{
+       winding_t       *w;
+       int                     refs = 0;
+       
+       
+       /* make a winding and filter it into the tree */
+       w = WindingFromDrawSurf( ds );
+       refs = FilterWindingIntoTree_r( w, ds, tree->headnode );
+       
+       /* return */
+       return refs;
+}
+
+
+
+/*
+FilterPatchIntoTree()
+subdivides a patch into an approximate curve and filters it into the tree
+*/
+
+#define        FILTER_SUBDIVISION              8
+
+static int FilterPatchIntoTree( mapDrawSurface_t *ds, tree_t *tree )
+{
+       int                                     i, x, y, refs;
+       mesh_t                          src, *mesh;
+       winding_t                       *w;
+       
+       
+       /* subdivide the surface */
+       src.width = ds->patchWidth;
+       src.height = ds->patchHeight;
+       src.verts = ds->verts;
+       mesh = SubdivideMesh( src, FILTER_SUBDIVISION, 32 );
+       
+       
+       /* filter each quad into the tree (fixme: use new patch x-triangulation code?) */
+       refs = 0;
+       for( y = 0; y < (mesh->height - 1); y++ )
+       {
+               for( x = 0; x < (mesh->width - 1); x++ )
+               {
+                       /* triangle 1 */
+                       w = AllocWinding( 3 );
+                       w->numpoints = 3;
+                       VectorCopy( mesh->verts[ y * mesh->width + x ].xyz, w->p[ 0 ] );
+                       VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 1 ] );
+                       VectorCopy( mesh->verts[ (y + 1) * mesh->width + x ].xyz, w->p[ 2 ] );
+                       refs += FilterWindingIntoTree_r( w, ds, tree->headnode );
+                       
+                       /* triangle 2 */
+                       w = AllocWinding( 3 );
+                       w->numpoints = 3;
+                       VectorCopy( mesh->verts[ y * mesh->width + x + 1 ].xyz, w->p[ 0 ] );
+                       VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x + 1 ].xyz, w->p[ 1 ] );
+                       VectorCopy( mesh->verts[ (y + 1 ) * mesh->width + x ].xyz, w->p[ 2 ] );
+                       refs += FilterWindingIntoTree_r( w, ds, tree->headnode );
+               }
+       }
+       
+       /* use point filtering as well */
+       for( i = 0; i < (mesh->width * mesh->height); i++ )
+               refs += FilterPointIntoTree_r( mesh->verts[ i ].xyz, ds, tree->headnode );
+       
+       /* free the subdivided mesh and return */
+       FreeMesh( mesh );
+       return refs;
+}
+
+
+
+/*
+FilterTrianglesIntoTree()
+filters a triangle surface (meta, model) into the bsp
+*/
+
+static int FilterTrianglesIntoTree( mapDrawSurface_t *ds, tree_t *tree )
+{
+       int                     i, refs;
+       winding_t       *w;
+       
+       
+       /* ydnar: gs mods: this was creating bogus triangles before */
+       refs = 0;
+       for( i = 0; i < ds->numIndexes; i += 3 )
+       {
+               /* error check */
+               if( ds->indexes[ i ] >= ds->numVerts ||
+                       ds->indexes[ i + 1 ] >= ds->numVerts ||
+                       ds->indexes[ i + 2 ] >= ds->numVerts )
+                       Error( "Index %d greater than vertex count %d", ds->indexes[ i ], ds->numVerts );
+               
+               /* make a triangle winding and filter it into the tree */
+               w = AllocWinding( 3 );
+               w->numpoints = 3;
+               VectorCopy( ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );
+               VectorCopy( ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );
+               VectorCopy( ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );
+               refs += FilterWindingIntoTree_r( w, ds, tree->headnode );
+       }
+       
+       /* use point filtering as well */
+       for( i = 0; i < ds->numVerts; i++ )
+               refs += FilterPointIntoTree_r( ds->verts[ i ].xyz, ds, tree->headnode );
+
+       return refs;
+}
+
+
+
+/*
+FilterFoliageIntoTree()
+filters a foliage surface (wolf et/splash damage)
+*/
+
+static int FilterFoliageIntoTree( mapDrawSurface_t *ds, tree_t *tree )
+{
+       int                             f, i, refs;
+       bspDrawVert_t   *instance;
+       vec3_t                  xyz;
+       winding_t               *w;
+       
+       
+       /* walk origin list */
+       refs = 0;
+       for( f = 0; f < ds->numFoliageInstances; f++ )
+       {
+               /* get instance */
+               instance = ds->verts + ds->patchHeight + f;
+               
+               /* walk triangle list */
+               for( i = 0; i < ds->numIndexes; i += 3 )
+               {
+                       /* error check */
+                       if( ds->indexes[ i ] >= ds->numVerts ||
+                               ds->indexes[ i + 1 ] >= ds->numVerts ||
+                               ds->indexes[ i + 2 ] >= ds->numVerts )
+                               Error( "Index %d greater than vertex count %d", ds->indexes[ i ], ds->numVerts );
+                       
+                       /* make a triangle winding and filter it into the tree */
+                       w = AllocWinding( 3 );
+                       w->numpoints = 3;
+                       VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );
+                       VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );
+                       VectorAdd( instance->xyz, ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );
+                       refs += FilterWindingIntoTree_r( w, ds, tree->headnode );
+               }
+               
+               /* use point filtering as well */
+               for( i = 0; i < (ds->numVerts - ds->numFoliageInstances); i++ )
+               {
+                       VectorAdd( instance->xyz, ds->verts[ i ].xyz, xyz );
+                       refs += FilterPointIntoTree_r( xyz, ds, tree->headnode );
+               }
+       }
+       
+       return refs;
+}
+
+
+
+/*
+FilterFlareIntoTree()
+simple point filtering for flare surfaces
+*/
+static int FilterFlareSurfIntoTree( mapDrawSurface_t *ds, tree_t *tree )
+{
+       return FilterPointIntoTree_r( ds->lightmapOrigin, ds, tree->headnode );
+}
+
+
+
+/*
+EmitDrawVerts() - ydnar
+emits bsp drawverts from a map drawsurface
+*/
+
+void EmitDrawVerts( mapDrawSurface_t *ds, bspDrawSurface_t *out )
+{
+       int                             i, k;
+       bspDrawVert_t   *dv;
+       shaderInfo_t    *si;
+       float                   offset;
+       
+       
+       /* get stuff */
+       si = ds->shaderInfo;
+       offset = si->offset;
+       
+       /* copy the verts */
+       out->firstVert = numBSPDrawVerts;
+       out->numVerts = ds->numVerts;
+       for( i = 0; i < ds->numVerts; i++ )
+       {
+               /* allocate a new vert */
+               if( numBSPDrawVerts == MAX_MAP_DRAW_VERTS )
+                       Error( "MAX_MAP_DRAW_VERTS" );
+               IncDrawVerts();
+               dv = &bspDrawVerts[ numBSPDrawVerts - 1 ];
+               
+               /* copy it */
+               memcpy( dv, &ds->verts[ i ], sizeof( *dv ) );
+               
+               /* offset? */
+               if( offset != 0.0f )
+                       VectorMA( dv->xyz, offset, dv->normal, dv->xyz );
+               
+               /* expand model bounds
+                  necessary because of misc_model surfaces on entities
+                  note: does not happen on worldspawn as its bounds is only used for determining lightgrid bounds */
+               if( numBSPModels > 0 )
+                       AddPointToBounds( dv->xyz, bspModels[ numBSPModels ].mins, bspModels[ numBSPModels ].maxs );
+               
+               /* debug color? */
+               if( debugSurfaces )
+               {
+                       for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                               VectorCopy( debugColors[ (ds - mapDrawSurfs) % 12 ], dv->color[ k ] );
+               }
+       }
+}
+
+
+
+/*
+FindDrawIndexes() - ydnar
+this attempts to find a run of indexes in the bsp that match the given indexes
+this tends to reduce the size of the bsp index pool by 1/3 or more
+returns numIndexes + 1 if the search failed
+*/
+
+int FindDrawIndexes( int numIndexes, int *indexes )
+{
+       int             i, j, numTestIndexes;
+       
+       
+       /* dummy check */
+       if( numIndexes < 3 || numBSPDrawIndexes < numIndexes || indexes == NULL )
+               return numBSPDrawIndexes;
+       
+       /* set limit */
+       numTestIndexes = 1 + numBSPDrawIndexes - numIndexes;
+       
+       /* handle 3 indexes as a special case for performance */
+       if( numIndexes == 3 )
+       {
+               /* run through all indexes */
+               for( i = 0; i < numTestIndexes; i++ )
+               {
+                       /* test 3 indexes */
+                       if( indexes[ 0 ] == bspDrawIndexes[ i ] &&
+                               indexes[ 1 ] == bspDrawIndexes[ i + 1 ] &&
+                               indexes[ 2 ] == bspDrawIndexes[ i + 2 ] )
+                       {
+                               numRedundantIndexes += numIndexes;
+                               return i;
+                       }
+               }
+               
+               /* failed */
+               return numBSPDrawIndexes;
+       }
+       
+       /* handle 4 or more indexes */
+       for( i = 0; i < numTestIndexes; i++ )
+       {
+               /* test first 4 indexes */
+               if( indexes[ 0 ] == bspDrawIndexes[ i ] &&
+                       indexes[ 1 ] == bspDrawIndexes[ i + 1 ] &&
+                       indexes[ 2 ] == bspDrawIndexes[ i + 2 ] &&
+                       indexes[ 3 ] == bspDrawIndexes[ i + 3 ] )
+               {
+                       /* handle 4 indexes */
+                       if( numIndexes == 4 )
+                               return i;
+                       
+                       /* test the remainder */
+                       for( j = 4; j < numIndexes; j++ )
+                       {
+                               if( indexes[ j ] != bspDrawIndexes[ i + j ] )
+                                       break;
+                               else if( j == (numIndexes - 1) )
+                               {
+                                       numRedundantIndexes += numIndexes;
+                                       return i;
+                               }
+                       }
+               }
+       }
+       
+       /* failed */
+       return numBSPDrawIndexes;
+}
+
+
+
+/*
+EmitDrawIndexes() - ydnar
+attempts to find an existing run of drawindexes before adding new ones
+*/
+
+void EmitDrawIndexes( mapDrawSurface_t *ds, bspDrawSurface_t *out )
+{
+       int                     i;
+       
+       
+       /* attempt to use redundant indexing */
+       out->firstIndex = FindDrawIndexes( ds->numIndexes, ds->indexes );
+       out->numIndexes = ds->numIndexes;
+       if( out->firstIndex == numBSPDrawIndexes )
+       {
+               /* copy new unique indexes */
+               for( i = 0; i < ds->numIndexes; i++ )
+               {
+                       if( numBSPDrawIndexes == MAX_MAP_DRAW_INDEXES )
+                               Error( "MAX_MAP_DRAW_INDEXES" );
+                       bspDrawIndexes[ numBSPDrawIndexes ] = ds->indexes[ i ];
+
+                       /* validate the index */
+                       if( ds->type != SURFACE_PATCH )
+                       {
+                               if( bspDrawIndexes[ numBSPDrawIndexes ] < 0 || bspDrawIndexes[ numBSPDrawIndexes ] >= ds->numVerts )
+                               {
+                                       Sys_Printf( "WARNING: %d %s has invalid index %d (%d)\n",
+                                               numBSPDrawSurfaces,
+                                               ds->shaderInfo->shader,
+                                               bspDrawIndexes[ numBSPDrawIndexes ],
+                                               i );
+                                       bspDrawIndexes[ numBSPDrawIndexes ] = 0;
+                               }
+                       }
+                       
+                       /* increment index count */
+                       numBSPDrawIndexes++;
+               }
+       }
+}
+
+
+
+
+/*
+EmitFlareSurface()
+emits a bsp flare drawsurface
+*/
+
+void EmitFlareSurface( mapDrawSurface_t *ds )
+{
+       int                                             i;
+       bspDrawSurface_t                *out;
+       
+       
+       /* ydnar: nuking useless flare drawsurfaces */
+       if( emitFlares == qfalse && ds->type != SURFACE_SHADER )
+               return;
+       
+       /* limit check */
+       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )
+               Error( "MAX_MAP_DRAW_SURFS" );
+       
+       /* allocate a new surface */
+       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )
+               Error( "MAX_MAP_DRAW_SURFS" );
+       out = &bspDrawSurfaces[ numBSPDrawSurfaces ];
+       ds->outputNum = numBSPDrawSurfaces;
+       numBSPDrawSurfaces++;
+       memset( out, 0, sizeof( *out ) );
+       
+       /* set it up */
+       out->surfaceType = MST_FLARE;
+       out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );
+       out->fogNum = ds->fogNum;
+       
+       /* RBSP */
+       for( i = 0; i < MAX_LIGHTMAPS; i++ )
+       {
+               out->lightmapNum[ i ] = -3;
+               out->lightmapStyles[ i ] = LS_NONE;
+               out->vertexStyles[ i ] = LS_NONE;
+       }
+       out->lightmapStyles[ 0 ] = ds->lightStyle;
+       out->vertexStyles[ 0 ] = ds->lightStyle;
+       
+       VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );                  /* origin */
+       VectorCopy( ds->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );    /* color */
+       VectorCopy( ds->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );
+       VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );    /* normal */
+       
+       /* add to count */
+       numSurfacesByType[ ds->type ]++;
+}
+
+
+
+/*
+EmitPatchSurface()
+emits a bsp patch drawsurface
+*/
+
+void EmitPatchSurface( mapDrawSurface_t *ds )
+{
+       int                                     i, j;
+       bspDrawSurface_t        *out;
+       int                                     surfaceFlags, contentFlags;
+       
+       
+       /* invert the surface if necessary */
+       if( ds->shaderInfo->invert )
+       {
+               bspDrawVert_t   *dv1, *dv2, temp;
+               
+
+               /* walk the verts, flip the normal */
+               for( i = 0; i < ds->numVerts; i++ )
+                       VectorScale( ds->verts[ i ].normal, -1.0f, ds->verts[ i ].normal );
+               
+               /* walk the verts again, but this time reverse their order */
+               for( j = 0; j < ds->patchHeight; j++ )
+               {
+                       for( i = 0; i < (ds->patchWidth / 2); i++ )
+                       {
+                               dv1 = &ds->verts[ j * ds->patchWidth + i ];
+                               dv2 = &ds->verts[ j * ds->patchWidth + (ds->patchWidth - i - 1) ];
+                               memcpy( &temp, dv1, sizeof( bspDrawVert_t ) );
+                               memcpy( dv1, dv2, sizeof( bspDrawVert_t ) );
+                               memcpy( dv2, &temp, sizeof( bspDrawVert_t ) );
+                       }
+               }
+               
+               /* invert facing */
+               VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );
+       }
+       
+       /* allocate a new surface */
+       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )
+               Error( "MAX_MAP_DRAW_SURFS" );
+       out = &bspDrawSurfaces[ numBSPDrawSurfaces ];
+       ds->outputNum = numBSPDrawSurfaces;
+       numBSPDrawSurfaces++;
+       memset( out, 0, sizeof( *out ) );
+       
+       /* set it up */
+       out->surfaceType = MST_PATCH;
+       if( debugSurfaces )
+               out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );
+       else if( patchMeta )
+       {
+               /* patch meta requires that we have nodraw patches for collision */
+               surfaceFlags = ds->shaderInfo->surfaceFlags;
+               contentFlags = ds->shaderInfo->contentFlags;
+               ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, NULL );
+               ApplySurfaceParm( "pointlight", &contentFlags, &surfaceFlags, NULL );
+               
+               /* we don't want this patch getting lightmapped */
+               VectorClear( ds->lightmapVecs[ 2 ] );
+               VectorClear( ds->lightmapAxis );
+               ds->sampleSize = 0;
+
+               /* emit the new fake shader */
+               out->shaderNum = EmitShader( ds->shaderInfo->shader, &contentFlags, &surfaceFlags );
+       }
+       else
+               out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );
+       out->patchWidth = ds->patchWidth;
+       out->patchHeight = ds->patchHeight;
+       out->fogNum = ds->fogNum;
+       
+       /* RBSP */
+       for( i = 0; i < MAX_LIGHTMAPS; i++ )
+       {
+               out->lightmapNum[ i ] = -3;
+               out->lightmapStyles[ i ] = LS_NONE;
+               out->vertexStyles[ i ] = LS_NONE;
+       }
+       out->lightmapStyles[ 0 ] = LS_NORMAL;
+       out->vertexStyles[ 0 ] = LS_NORMAL;
+       
+       /* ydnar: gs mods: previously, the lod bounds were stored in lightmapVecs[ 0 ] and [ 1 ], moved to bounds[ 0 ] and [ 1 ] */
+       VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );
+       VectorCopy( ds->bounds[ 0 ], out->lightmapVecs[ 0 ] );
+       VectorCopy( ds->bounds[ 1 ], out->lightmapVecs[ 1 ] );
+       VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );
+       
+       /* ydnar: gs mods: clear out the plane normal */
+       if( ds->planar == qfalse )
+               VectorClear( out->lightmapVecs[ 2 ] );
+       
+       /* emit the verts and indexes */
+       EmitDrawVerts( ds, out );
+       EmitDrawIndexes( ds, out );
+       
+       /* add to count */
+       numSurfacesByType[ ds->type ]++;
+}
+
+
+
+/*
+OptimizeTriangleSurface() - ydnar
+optimizes the vertex/index data in a triangle surface
+*/
+
+#define VERTEX_CACHE_SIZE      16
+
+static void OptimizeTriangleSurface( mapDrawSurface_t *ds )
+{
+       int             i, j, k, temp, first, best, bestScore, score;
+       int             vertexCache[ VERTEX_CACHE_SIZE + 1 ];   /* one more for optimizing insert */
+       int             *indexes;
+       
+       
+       /* certain surfaces don't get optimized */
+       if( ds->numIndexes <= VERTEX_CACHE_SIZE ||
+               ds->shaderInfo->autosprite )
+               return;
+       
+       /* create index scratch pad */
+       indexes = safe_malloc( ds->numIndexes * sizeof( *indexes ) );
+       memcpy( indexes, ds->indexes, ds->numIndexes * sizeof( *indexes ) );
+       
+       /* setup */
+       for( i = 0; i <= VERTEX_CACHE_SIZE && i < ds->numIndexes; i++ )
+               vertexCache[ i ] = indexes[ i ];
+       
+       /* add triangles in a vertex cache-aware order */
+       for( i = 0; i < ds->numIndexes; i += 3 )
+       {
+               /* find best triangle given the current vertex cache */
+               first = -1;
+               best = -1;
+               bestScore = -1;
+               for( j = 0; j < ds->numIndexes; j += 3 )
+               {
+                       /* valid triangle? */
+                       if( indexes[ j ] != -1 )
+                       {
+                               /* set first if necessary */
+                               if( first < 0 )
+                                       first = j;
+                               
+                               /* score the triangle */
+                               score = 0;
+                               for( k = 0; k < VERTEX_CACHE_SIZE; k++ )
+                               {
+                                       if( indexes[ j ] == vertexCache[ k ] || indexes[ j + 1 ] == vertexCache[ k ] || indexes[ j + 2 ] == vertexCache[ k ] )
+                                               score++;
+                               }
+                               
+                               /* better triangle? */
+                               if( score > bestScore )
+                               {
+                                       bestScore = score;
+                                       best = j;
+                               }
+                               
+                               /* a perfect score of 3 means this triangle's verts are already present in the vertex cache */
+                               if( score == 3 )
+                                       break;
+                       }
+               }
+               
+               /* check if no decent triangle was found, and use first available */
+               if( best < 0 )
+                       best = first;
+               
+               /* valid triangle? */
+               if( best >= 0 )
+               {
+                       /* add triangle to vertex cache */
+                       for( j = 0; j < 3; j++ )
+                       {
+                               for( k = 0; k < VERTEX_CACHE_SIZE; k++ )
+                               {
+                                       if( indexes[ best + j ] == vertexCache[ k ] )
+                                               break;
+                               }
+                               
+                               if( k >= VERTEX_CACHE_SIZE )
+                               {
+                                       /* pop off top of vertex cache */
+                                       for( k = VERTEX_CACHE_SIZE; k > 0; k-- )
+                                               vertexCache[ k ] = vertexCache[ k - 1 ];
+                                       
+                                       /* add vertex */
+                                       vertexCache[ 0 ] = indexes[ best + j ];
+                               }
+                       }
+                       
+                       /* add triangle to surface */
+                       ds->indexes[ i ] = indexes[ best ];
+                       ds->indexes[ i + 1 ] = indexes[ best + 1 ];
+                       ds->indexes[ i + 2 ] = indexes[ best + 2 ];
+                       
+                       /* clear from input pool */
+                       indexes[ best ] = -1;
+                       indexes[ best + 1 ] = -1;
+                       indexes[ best + 2 ] = -1;
+                       
+                       /* sort triangle windings (312 -> 123) */
+                       while( ds->indexes[ i ] > ds->indexes[ i + 1 ] || ds->indexes[ i ] > ds->indexes[ i + 2 ] )
+                       {
+                               temp = ds->indexes[ i ];
+                               ds->indexes[ i ] = ds->indexes[ i + 1 ];
+                               ds->indexes[ i + 1 ] = ds->indexes[ i + 2 ];
+                               ds->indexes[ i + 2 ] = temp;
+                       }
+               }
+       }
+       
+       /* clean up */
+       free( indexes );
+}
+
+
+
+/*
+EmitTriangleSurface()
+creates a bsp drawsurface from arbitrary triangle surfaces
+*/
+
+static void EmitTriangleSurface( mapDrawSurface_t *ds )
+{
+       int                                             i, temp;
+       bspDrawSurface_t                *out;
+       
+       
+       /* invert the surface if necessary */
+       if( ds->shaderInfo->invert )
+       {
+               /* walk the indexes, reverse the triangle order */
+               for( i = 0; i < ds->numIndexes; i += 3 )
+               {
+                       temp = ds->indexes[ i ];
+                       ds->indexes[ i ] = ds->indexes[ i + 1 ];
+                       ds->indexes[ i + 1 ] = temp;
+               }
+               
+               /* walk the verts, flip the normal */
+               for( i = 0; i < ds->numVerts; i++ )
+                       VectorScale( ds->verts[ i ].normal, -1.0f, ds->verts[ i ].normal );
+               
+               /* invert facing */
+               VectorScale( ds->lightmapVecs[ 2 ], -1.0f, ds->lightmapVecs[ 2 ] );
+       }
+       
+       /* allocate a new surface */
+       if( numBSPDrawSurfaces == MAX_MAP_DRAW_SURFS )
+               Error( "MAX_MAP_DRAW_SURFS" );
+       out = &bspDrawSurfaces[ numBSPDrawSurfaces ];
+       ds->outputNum = numBSPDrawSurfaces;
+       numBSPDrawSurfaces++;
+       memset( out, 0, sizeof( *out ) );
+       
+       /* ydnar/sd: handle wolf et foliage surfaces */
+       if( ds->type == SURFACE_FOLIAGE )
+               out->surfaceType = MST_FOLIAGE;
+       
+       /* ydnar: gs mods: handle lightmapped terrain (force to planar type) */
+       //%     else if( VectorLength( ds->lightmapAxis ) <= 0.0f || ds->type == SURFACE_TRIANGLES || ds->type == SURFACE_FOGHULL || debugSurfaces )
+       else if( (VectorLength( ds->lightmapAxis ) <= 0.0f && ds->planar == qfalse) || ds->type == SURFACE_TRIANGLES || ds->type == SURFACE_FOGHULL || debugSurfaces )
+               out->surfaceType = MST_TRIANGLE_SOUP;
+       
+       /* set to a planar face */
+       else
+               out->surfaceType = MST_PLANAR;
+       
+       /* set it up */
+       if( debugSurfaces )
+               out->shaderNum = EmitShader( "debugsurfaces", NULL, NULL );
+       else
+               out->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );
+       out->patchWidth = ds->patchWidth;
+       out->patchHeight = ds->patchHeight;
+       out->fogNum = ds->fogNum;
+       
+       /* debug inset (push each triangle vertex towards the center of each triangle it is on */
+       if( debugInset )
+       {
+               bspDrawVert_t   *a, *b, *c;
+               vec3_t                  cent, dir;
+
+               
+               /* walk triangle list */
+               for( i = 0; i < ds->numIndexes; i += 3 )
+               {
+                       /* get verts */
+                       a = &ds->verts[ ds->indexes[ i ] ];
+                       b = &ds->verts[ ds->indexes[ i + 1 ] ];
+                       c = &ds->verts[ ds->indexes[ i + 2 ] ];
+                       
+                       /* calculate centroid */
+                       VectorCopy( a->xyz, cent );
+                       VectorAdd( cent, b->xyz, cent );
+                       VectorAdd( cent, c->xyz, cent );
+                       VectorScale( cent, 1.0f / 3.0f, cent );
+                       
+                       /* offset each vertex */
+                       VectorSubtract( cent, a->xyz, dir );
+                       VectorNormalize( dir, dir );
+                       VectorAdd( a->xyz, dir, a->xyz );
+                       VectorSubtract( cent, b->xyz, dir );
+                       VectorNormalize( dir, dir );
+                       VectorAdd( b->xyz, dir, b->xyz );
+                       VectorSubtract( cent, c->xyz, dir );
+                       VectorNormalize( dir, dir );
+                       VectorAdd( c->xyz, dir, c->xyz );
+               }
+       }
+       
+       /* RBSP */
+       for( i = 0; i < MAX_LIGHTMAPS; i++ )
+       {
+               out->lightmapNum[ i ] = -3;
+               out->lightmapStyles[ i ] = LS_NONE;
+               out->vertexStyles[ i ] = LS_NONE;
+       }
+       out->lightmapStyles[ 0 ] = LS_NORMAL;
+       out->vertexStyles[ 0 ] = LS_NORMAL;
+       
+       /* lightmap vectors (lod bounds for patches */
+       VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );
+       VectorCopy( ds->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );
+       VectorCopy( ds->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );
+       VectorCopy( ds->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );
+       
+       /* ydnar: gs mods: clear out the plane normal */
+       if( ds->planar == qfalse )
+               VectorClear( out->lightmapVecs[ 2 ] );
+       
+       /* optimize the surface's triangles */
+       OptimizeTriangleSurface( ds );
+       
+       /* emit the verts and indexes */
+       EmitDrawVerts( ds, out );
+       EmitDrawIndexes( ds, out );
+       
+       /* add to count */
+       numSurfacesByType[ ds->type ]++;
+}
+
+
+
+/*
+EmitFaceSurface()
+emits a bsp planar winding (brush face) drawsurface
+*/
+
+static void EmitFaceSurface( mapDrawSurface_t *ds )
+{
+       /* strip/fan finding was moved elsewhere */
+       StripFaceSurface( ds );
+       EmitTriangleSurface( ds );
+}
+
+
+
+/*
+MakeDebugPortalSurfs_r() - ydnar
+generates drawsurfaces for passable portals in the bsp
+*/
+
+static void MakeDebugPortalSurfs_r( node_t *node, shaderInfo_t *si )
+{
+       int                                     i, k, c, s;     
+       portal_t                        *p;
+       winding_t                       *w;
+       mapDrawSurface_t        *ds;
+       bspDrawVert_t           *dv;
+       
+       
+       /* recurse if decision node */
+       if( node->planenum != PLANENUM_LEAF)
+       {
+               MakeDebugPortalSurfs_r( node->children[ 0 ], si );
+               MakeDebugPortalSurfs_r( node->children[ 1 ], si );
+               return;
+       }
+       
+       /* don't bother with opaque leaves */
+       if( node->opaque )
+               return;
+       
+       /* walk the list of portals */
+       for( c = 0, p = node->portals; p != NULL; c++, p = p->next[ s ] )
+       {
+               /* get winding and side even/odd */
+               w = p->winding;
+               s = (p->nodes[ 1 ] == node);
+               
+               /* is this a valid portal for this leaf? */
+               if( w && p->nodes[ 0 ] == node )
+               {
+                       /* is this portal passable? */
+                       if( PortalPassable( p ) == qfalse )
+                               continue;
+                       
+                       /* check max points */
+                       if( w->numpoints > 64 )
+                               Error( "MakePortalSurfs_r: w->numpoints = %d", w->numpoints );
+                       
+                       /* allocate a drawsurface */
+                       ds = AllocDrawSurface( SURFACE_FACE );
+                       ds->shaderInfo = si;
+                       ds->planar = qtrue;
+                       ds->sideRef = AllocSideRef( p->side, NULL );
+                       ds->planeNum = FindFloatPlane( p->plane.normal, p->plane.dist, 0, NULL );
+                       VectorCopy( p->plane.normal, ds->lightmapVecs[ 2 ] );
+                       ds->fogNum = -1;
+                       ds->numVerts = w->numpoints;
+                       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
+                       memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
+                       
+                       /* walk the winding */
+                       for( i = 0; i < ds->numVerts; i++ )
+                       {
+                               /* get vert */
+                               dv = ds->verts + i;
+                               
+                               /* set it */
+                               VectorCopy( w->p[ i ], dv->xyz );
+                               VectorCopy( p->plane.normal, dv->normal );
+                               dv->st[ 0 ] = 0;
+                               dv->st[ 1 ] = 0;
+                               for( k = 0; k < MAX_LIGHTMAPS; k++ )
+                               {
+                                       VectorCopy( debugColors[ c % 12 ], dv->color[ k ] );
+                                       dv->color[ k ][ 3 ] = 32;
+                               }
+                       }
+               }
+       }
+}
+
+
+
+/*
+MakeDebugPortalSurfs() - ydnar
+generates drawsurfaces for passable portals in the bsp
+*/
+
+void MakeDebugPortalSurfs( tree_t *tree )
+{
+       shaderInfo_t    *si;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- MakeDebugPortalSurfs ---\n" );
+       
+       /* get portal debug shader */
+       si = ShaderInfoForShader( "debugportals" );
+       
+       /* walk the tree */
+       MakeDebugPortalSurfs_r( tree->headnode, si );
+}
+
+
+
+/*
+MakeFogHullSurfs()
+generates drawsurfaces for a foghull (this MUST use a sky shader)
+*/
+
+void MakeFogHullSurfs( entity_t *e, tree_t *tree, char *shader )
+{
+       shaderInfo_t            *si;
+       mapDrawSurface_t        *ds;
+       vec3_t                          fogMins, fogMaxs;
+       int                                     i, indexes[] =
+                                               {
+                                                       0, 1, 2, 0, 2, 3,
+                                                       4, 7, 5, 5, 7, 6,
+                                                       1, 5, 6, 1, 6, 2,
+                                                       0, 4, 5, 0, 5, 1,
+                                                       2, 6, 7, 2, 7, 3,
+                                                       3, 7, 4, 3, 4, 0
+                                               };
+
+       
+       /* dummy check */
+       if( shader == NULL || shader[ 0 ] == '\0' )
+               return;
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- MakeFogHullSurfs ---\n" );
+       
+       /* get hull bounds */
+       VectorCopy( mapMins, fogMins );
+       VectorCopy( mapMaxs, fogMaxs );
+       for( i = 0; i < 3; i++ )
+       {
+               fogMins[ i ] -= 128;
+               fogMaxs[ i ] += 128;
+       }
+       
+       /* get foghull shader */
+       si = ShaderInfoForShader( shader );
+       
+       /* allocate a drawsurface */
+       ds = AllocDrawSurface( SURFACE_FOGHULL );
+       ds->shaderInfo = si;
+       ds->fogNum = -1;
+       ds->numVerts = 8;
+       ds->verts = safe_malloc( ds->numVerts * sizeof( *ds->verts ) );
+       memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
+       ds->numIndexes = 36;
+       ds->indexes = safe_malloc( ds->numIndexes * sizeof( *ds->indexes ) );
+       memset( ds->indexes, 0, ds->numIndexes * sizeof( *ds->indexes ) );
+       
+       /* set verts */
+       VectorSet( ds->verts[ 0 ].xyz, fogMins[ 0 ], fogMins[ 1 ], fogMins[ 2 ] );
+       VectorSet( ds->verts[ 1 ].xyz, fogMins[ 0 ], fogMaxs[ 1 ], fogMins[ 2 ] );
+       VectorSet( ds->verts[ 2 ].xyz, fogMaxs[ 0 ], fogMaxs[ 1 ], fogMins[ 2 ] );
+       VectorSet( ds->verts[ 3 ].xyz, fogMaxs[ 0 ], fogMins[ 1 ], fogMins[ 2 ] );
+       
+       VectorSet( ds->verts[ 4 ].xyz, fogMins[ 0 ], fogMins[ 1 ], fogMaxs[ 2 ] );
+       VectorSet( ds->verts[ 5 ].xyz, fogMins[ 0 ], fogMaxs[ 1 ], fogMaxs[ 2 ] );
+       VectorSet( ds->verts[ 6 ].xyz, fogMaxs[ 0 ], fogMaxs[ 1 ], fogMaxs[ 2 ] );
+       VectorSet( ds->verts[ 7 ].xyz, fogMaxs[ 0 ], fogMins[ 1 ], fogMaxs[ 2 ] );
+       
+       /* set indexes */
+       memcpy( ds->indexes, indexes, ds->numIndexes * sizeof( *ds->indexes ) );
+}
+
+
+
+/*
+BiasSurfaceTextures()
+biases a surface's texcoords as close to 0 as possible
+*/
+
+void BiasSurfaceTextures( mapDrawSurface_t *ds )
+{
+       int             i;
+       
+       
+       /* calculate the surface texture bias */
+       CalcSurfaceTextureRange( ds );
+       
+       /* don't bias globaltextured shaders */
+       if( ds->shaderInfo->globalTexture )
+               return;
+       
+       /* bias the texture coordinates */
+       for( i = 0; i < ds->numVerts; i++ )
+       {
+               ds->verts[ i ].st[ 0 ] += ds->bias[ 0 ];
+               ds->verts[ i ].st[ 1 ] += ds->bias[ 1 ];
+       }
+}
+
+
+
+/*
+AddSurfaceModelsToTriangle_r()
+adds models to a specified triangle, returns the number of models added
+*/
+
+int AddSurfaceModelsToTriangle_r( mapDrawSurface_t *ds, surfaceModel_t *model, bspDrawVert_t **tri )
+{
+       bspDrawVert_t   mid, *tri2[ 3 ];
+       int                             max, n, localNumSurfaceModels;
+       
+       
+       /* init */
+       localNumSurfaceModels = 0;
+       
+       /* subdivide calc */
+       {
+               int                     i;
+               float           *a, *b, dx, dy, dz, dist, maxDist;
+               
+               
+               /* find the longest edge and split it */
+               max = -1;
+               maxDist = 0.0f;
+               for( i = 0; i < 3; i++ )
+               {
+                       /* get verts */
+                       a = tri[ i ]->xyz;
+                       b = tri[ (i + 1) % 3 ]->xyz;
+                       
+                       /* get dists */
+                       dx = a[ 0 ] - b[ 0 ];
+                       dy = a[ 1 ] - b[ 1 ];
+                       dz = a[ 2 ] - b[ 2 ];
+                       dist = (dx * dx) + (dy * dy) + (dz * dz);
+                       
+                       /* longer? */
+                       if( dist > maxDist )
+                       {
+                               maxDist = dist;
+                               max = i;
+                       }
+               }
+               
+               /* is the triangle small enough? */
+               if( max < 0 || maxDist <= (model->density * model->density) )
+               {
+                       float   odds, r, angle;
+                       vec3_t  origin, normal, scale, axis[ 3 ], angles;
+                       m4x4_t  transform, temp;
+
+                       
+                       /* roll the dice (model's odds scaled by vertex alpha) */
+                       odds = model->odds * (tri[ 0 ]->color[ 0 ][ 3 ] + tri[ 0 ]->color[ 0 ][ 3 ] + tri[ 0 ]->color[ 0 ][ 3 ]) / 765.0f;
+                       r = Random();
+                       if( r > model->odds )
+                               return 0;
+                       
+                       /* calculate scale */
+                       r = model->minScale + Random() * (model->maxScale - model->minScale);
+                       VectorSet( scale, r, r, r );
+                       
+                       /* calculate angle */
+                       angle = model->minAngle + Random() * (model->maxAngle - model->minAngle);
+                       
+                       /* calculate average origin */
+                       VectorCopy( tri[ 0 ]->xyz, origin );
+                       VectorAdd( origin, tri[ 1 ]->xyz, origin );
+                       VectorAdd( origin, tri[ 2 ]->xyz, origin );
+                       VectorScale( origin, (1.0f / 3.0f), origin );
+                       
+                       /* clear transform matrix */
+                       m4x4_identity( transform );
+
+                       /* handle oriented models */
+                       if( model->oriented )
+                       {
+                               /* set angles */
+                               VectorSet( angles, 0.0f, 0.0f, angle );
+                               
+                               /* calculate average normal */
+                               VectorCopy( tri[ 0 ]->normal, normal );
+                               VectorAdd( normal, tri[ 1 ]->normal, normal );
+                               VectorAdd( normal, tri[ 2 ]->normal, normal );
+                               if( VectorNormalize( normal, axis[ 2 ] ) == 0.0f )
+                                       VectorCopy( tri[ 0 ]->normal, axis[ 2 ] );
+                               
+                               /* make perpendicular vectors */
+                               MakeNormalVectors( axis[ 2 ], axis[ 1 ], axis[ 0 ] );
+                               
+                               /* copy to matrix */
+                               m4x4_identity( temp );
+                               temp[ 0 ] = axis[ 0 ][ 0 ];     temp[ 1 ] = axis[ 0 ][ 1 ];     temp[ 2 ] = axis[ 0 ][ 2 ];
+                               temp[ 4 ] = axis[ 1 ][ 0 ];     temp[ 5 ] = axis[ 1 ][ 1 ];     temp[ 6 ] = axis[ 1 ][ 2 ];
+                               temp[ 8 ] = axis[ 2 ][ 0 ];     temp[ 9 ] = axis[ 2 ][ 1 ];     temp[ 10 ] = axis[ 2 ][ 2 ];
+                               
+                               /* scale */
+                               m4x4_scale_by_vec3( temp, scale );
+                               
+                               /* rotate around z axis */
+                               m4x4_rotate_by_vec3( temp, angles, eXYZ );
+                               
+                               /* translate */
+                               m4x4_translate_by_vec3( transform, origin );
+                               
+                               /* tranform into axis space */
+                               m4x4_multiply_by_m4x4( transform, temp );
+                       }
+                       
+                       /* handle z-up models */
+                       else
+                       {
+                               /* set angles */
+                               VectorSet( angles, 0.0f, 0.0f, angle );
+                               
+                               /* set matrix */
+                               m4x4_pivoted_transform_by_vec3( transform, origin, angles, eXYZ, scale, vec3_origin );
+                       }
+                       
+                       /* insert the model */
+                       InsertModel( (char *) model->model, 0, transform, NULL, ds->celShader, ds->entityNum, ds->castShadows, ds->recvShadows, 0, ds->lightmapScale );
+                       
+                       /* return to sender */
+                       return 1;
+               }
+       }
+       
+       /* split the longest edge and map it */
+       LerpDrawVert( tri[ max ], tri[ (max + 1) % 3 ], &mid );
+       
+       /* recurse to first triangle */
+       VectorCopy( tri, tri2 );
+       tri2[ max ] = &mid;
+       n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );
+       if( n < 0 )
+               return n;
+       localNumSurfaceModels += n;
+       
+       /* recurse to second triangle */
+       VectorCopy( tri, tri2 );
+       tri2[ (max + 1) % 3 ] = &mid;
+       n = AddSurfaceModelsToTriangle_r( ds, model, tri2 );
+       if( n < 0 )
+               return n;
+       localNumSurfaceModels += n;
+       
+       /* return count */
+       return localNumSurfaceModels;
+}
+
+
+
+/*
+AddSurfaceModels()
+adds a surface's shader models to the surface
+*/
+
+int AddSurfaceModels( mapDrawSurface_t *ds )
+{
+       surfaceModel_t  *model;
+       int                             i, x, y, n, pw[ 5 ], r, localNumSurfaceModels, iterations;
+       mesh_t                  src, *mesh, *subdivided;
+       bspDrawVert_t   centroid, *tri[ 3 ];
+       float                   alpha;
+       
+       
+       /* dummy check */
+       if( ds == NULL || ds->shaderInfo == NULL || ds->shaderInfo->surfaceModel == NULL )
+               return 0;
+       
+       /* init */
+       localNumSurfaceModels = 0;
+       
+       /* walk the model list */
+       for( model = ds->shaderInfo->surfaceModel; model != NULL; model = model->next )
+       {
+               /* switch on type */
+               switch( ds->type )
+               {
+                       /* handle brush faces and decals */
+                       case SURFACE_FACE:
+                       case SURFACE_DECAL:
+                               /* calculate centroid */
+                               memset( &centroid, 0, sizeof( centroid ) );
+                               alpha = 0.0f;
+                               
+                               /* walk verts */
+                               for( i = 0; i < ds->numVerts; i++ )
+                               {
+                                       VectorAdd( centroid.xyz, ds->verts[ i ].xyz, centroid.xyz );
+                                       VectorAdd( centroid.normal, ds->verts[ i ].normal, centroid.normal );
+                                       centroid.st[ 0 ] += ds->verts[ i ].st[ 0 ];
+                                       centroid.st[ 1 ] += ds->verts[ i ].st[ 1 ];
+                                       alpha += ds->verts[ i ].color[ 0 ][ 3 ];
+                               }
+                               
+                               /* average */
+                               centroid.xyz[ 0 ] /= ds->numVerts;
+                               centroid.xyz[ 1 ] /= ds->numVerts;
+                               centroid.xyz[ 2 ] /= ds->numVerts;
+                               if( VectorNormalize( centroid.normal, centroid.normal ) == 0.0f )
+                                       VectorCopy( ds->verts[ 0 ].normal, centroid.normal );
+                               centroid.st[ 0 ]  /= ds->numVerts;
+                               centroid.st[ 1 ]  /= ds->numVerts;
+                               alpha /= ds->numVerts;
+                               centroid.color[ 0 ][ 0 ] = 0xFF;
+                               centroid.color[ 0 ][ 1 ] = 0xFF;
+                               centroid.color[ 0 ][ 2 ] = 0xFF;
+                               centroid.color[ 0 ][ 2 ] = (alpha > 255.0f ? 0xFF : alpha);
+                               
+                               /* head vert is centroid */
+                               tri[ 0 ] = &centroid;
+                               
+                               /* walk fanned triangles */
+                               for( i = 0; i < ds->numVerts; i++ )
+                               {
+                                       /* set triangle */
+                                       tri[ 1 ] = &ds->verts[ i ];
+                                       tri[ 2 ] = &ds->verts[ (i + 1) % ds->numVerts ];
+                                       
+                                       /* create models */
+                                       n = AddSurfaceModelsToTriangle_r( ds, model, tri );
+                                       if( n < 0 )
+                                               return n;
+                                       localNumSurfaceModels += n;
+                               }
+                               break;
+                       
+                       /* handle patches */
+                       case SURFACE_PATCH:
+                               /* subdivide the surface */
+                               src.width = ds->patchWidth;
+                               src.height = ds->patchHeight;
+                               src.verts = ds->verts;
+                               //%     subdivided = SubdivideMesh( src, 8.0f, 512 );
+                               iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );
+                               subdivided = SubdivideMesh2( src, iterations );
+                               
+                               /* fit it to the curve and remove colinear verts on rows/columns */
+                               PutMeshOnCurve( *subdivided );
+                               mesh = RemoveLinearMeshColumnsRows( subdivided );
+                               FreeMesh( subdivided );
+                               
+                               /* subdivide each quad to place the models */
+                               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;
+                                               
+                                               /* triangle 1 */
+                                               tri[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
+                                               tri[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];
+                                               tri[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];
+                                               n = AddSurfaceModelsToTriangle_r( ds, model, tri );
+                                               if( n < 0 )
+                                                       return n;
+                                               localNumSurfaceModels += n;
+                                               
+                                               /* triangle 2 */
+                                               tri[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
+                                               tri[ 1 ] = &mesh->verts[ pw[ r + 2 ] ];
+                                               tri[ 2 ] = &mesh->verts[ pw[ r + 3 ] ];
+                                               n = AddSurfaceModelsToTriangle_r( ds, model, tri );
+                                               if( n < 0 )
+                                                       return n;
+                                               localNumSurfaceModels += n;
+                                       }
+                               }
+                               
+                               /* free the subdivided mesh */
+                               FreeMesh( mesh );
+                               break;
+                       
+                       /* handle triangle surfaces */
+                       case SURFACE_TRIANGLES:
+                       case SURFACE_FORCED_META:
+                       case SURFACE_META:
+                               /* walk the triangle list */
+                               for( i = 0; i < ds->numIndexes; i += 3 )
+                               {
+                                       tri[ 0 ] = &ds->verts[ ds->indexes[ i ] ];
+                                       tri[ 1 ] = &ds->verts[ ds->indexes[ i + 1 ] ];
+                                       tri[ 2 ] = &ds->verts[ ds->indexes[ i + 2 ] ];
+                                       n = AddSurfaceModelsToTriangle_r( ds, model, tri );
+                                       if( n < 0 )
+                                               return n;
+                                       localNumSurfaceModels += n;
+                               }
+                               break;
+                       
+                       /* no support for flares, foghull, etc */
+                       default:
+                               break;
+               }
+       }
+       
+       /* return count */
+       return localNumSurfaceModels;
+}
+
+
+
+/*
+AddEntitySurfaceModels() - ydnar
+adds surfacemodels to an entity's surfaces
+*/
+
+void AddEntitySurfaceModels( entity_t *e )
+{
+       int             i;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- AddEntitySurfaceModels ---\n" );
+       
+       /* walk the surface list */
+       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
+               numSurfaceModels += AddSurfaceModels( &mapDrawSurfs[ i ] );
+}
+
+
+
+/*
+FilterDrawsurfsIntoTree()
+upon completion, all drawsurfs that actually generate a reference
+will have been emited to the bspfile arrays, and the references
+will have valid final indexes
+*/
+
+void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree )
+{
+       int                                     i, j;
+       mapDrawSurface_t        *ds;
+       shaderInfo_t            *si;
+       vec3_t                          origin, mins, maxs;
+       int                                     refs;
+       int                                     numSurfs, numRefs, numSkyboxSurfaces;
+       
+       
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- FilterDrawsurfsIntoTree ---\n" );
+       
+       /* filter surfaces into the tree */
+       numSurfs = 0;
+       numRefs = 0;
+       numSkyboxSurfaces = 0;
+       for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
+       {
+               /* get surface and try to early out */
+               ds = &mapDrawSurfs[ i ];
+               if( ds->numVerts == 0 && ds->type != SURFACE_FLARE && ds->type != SURFACE_SHADER )
+                       continue;
+               
+               /* get shader */
+               si = ds->shaderInfo;
+               
+               /* ydnar: skybox surfaces are special */
+               if( ds->skybox )
+               {
+                       refs = AddReferenceToTree_r( ds, tree->headnode, qtrue );
+                       ds->skybox = qfalse;
+               }
+               else
+               {
+                       /* refs initially zero */
+                       refs = 0;
+                       
+                       /* ydnar: apply alphamod */
+                       AlphaMod( ds->shaderInfo->alphaMod, ds->numVerts, ds->verts );
+                       
+                       /* apply texture coordinate mods */
+                       for( j = 0; j < ds->numVerts; j++ )
+                               TcMod( si->mod, ds->verts[ j ].st );
+                       
+                       /* ydnar: make fur surfaces */
+                       if( si->furNumLayers > 0 )
+                               Fur( ds );
+                       
+                       /* ydnar/sd: make foliage surfaces */
+                       if( si->foliage != NULL )
+                               Foliage( ds );
+                       
+                       /* create a flare surface if necessary */
+                       if( si->flareShader[ 0 ] )
+                               AddSurfaceFlare( ds, e->origin );
+                       
+                       /* ydnar: don't emit nodraw surfaces (like nodraw fog) */
+                       if( si != NULL && (si->compileFlags & C_NODRAW) && ds->type != SURFACE_PATCH )
+                               continue;
+                       
+                       /* ydnar: bias the surface textures */
+                       BiasSurfaceTextures( ds );
+                       
+                       /* ydnar: globalizing of fog volume handling (eek a hack) */
+                       if( e != entities && si->noFog == qfalse )
+                       {
+                               /* find surface origin and offset by entity origin */
+                               VectorAdd( ds->mins, ds->maxs, origin );
+                               VectorScale( origin, 0.5f, origin );
+                               VectorAdd( origin, e->origin, origin );
+                               
+                               VectorAdd( ds->mins, e->origin, mins );
+                               VectorAdd( ds->maxs, e->origin, maxs );
+                               
+                               /* set the fog number for this surface */
+                               ds->fogNum = FogForBounds( mins, maxs, 1.0f );  //%     FogForPoint( origin, 0.0f );
+                       }
+               }
+               
+               /* ydnar: gs mods: handle the various types of surfaces */
+               switch( ds->type )
+               {
+                       /* handle brush faces */
+                       case SURFACE_FACE:
+                       case SURFACE_DECAL:
+                               if( refs == 0 )
+                                       refs = FilterFaceIntoTree( ds, tree );
+                               if( refs > 0 )
+                                       EmitFaceSurface( ds );
+                               break;
+                       
+                       /* handle patches */
+                       case SURFACE_PATCH:
+                               if( refs == 0 )
+                                       refs = FilterPatchIntoTree( ds, tree );
+                               if( refs > 0 )
+                                       EmitPatchSurface( ds );
+                               break;
+                       
+                       /* handle triangle surfaces */
+                       case SURFACE_TRIANGLES:
+                       case SURFACE_FORCED_META:
+                       case SURFACE_META:
+                               //%     Sys_FPrintf( SYS_VRB, "Surface %4d: [%1d] %4d verts %s\n", numSurfs, ds->planar, ds->numVerts, si->shader );
+                               if( refs == 0 )
+                                       refs = FilterTrianglesIntoTree( ds, tree );
+                               if( refs > 0 )
+                                       EmitTriangleSurface( ds );
+                               break;
+                       
+                       /* handle foliage surfaces (splash damage/wolf et) */
+                       case SURFACE_FOLIAGE:
+                               //%     Sys_FPrintf( SYS_VRB, "Surface %4d: [%d] %4d verts %s\n", numSurfs, ds->numFoliageInstances, ds->numVerts, si->shader );
+                               if( refs == 0 )
+                                       refs = FilterFoliageIntoTree( ds, tree );
+                               if( refs > 0 )
+                                       EmitTriangleSurface( ds );
+                               break;
+                       
+                       /* handle foghull surfaces */
+                       case SURFACE_FOGHULL:
+                               if( refs == 0 )
+                                       refs = AddReferenceToTree_r( ds, tree->headnode, qfalse );
+                               if( refs > 0 )
+                                       EmitTriangleSurface( ds );
+                               break;
+                       
+                       /* handle flares */
+                       case SURFACE_FLARE:
+                               if( refs == 0 )
+                                       refs = FilterFlareSurfIntoTree( ds, tree );
+                               if( refs > 0 )
+                                       EmitFlareSurface( ds );
+                               break;
+                       
+                       /* handle shader-only surfaces */
+                       case SURFACE_SHADER:
+                               refs = 1;
+                               EmitFlareSurface( ds );
+                               break;
+                       
+                       /* no references */
+                       default:
+                               refs = 0;
+                               break;
+               }
+               
+               /* tot up the references */
+               if( refs > 0 )
+               {
+                       /* tot up counts */
+                       numSurfs++;
+                       numRefs += refs;
+                       
+                       /* emit extra surface data */
+                       SetSurfaceExtra( ds, numBSPDrawSurfaces - 1 );
+                       //%     Sys_FPrintf( SYS_VRB, "%d verts %d indexes\n", ds->numVerts, ds->numIndexes );
+                       
+                       /* one last sanity check */
+                       {
+                               bspDrawSurface_t        *out;
+                               out = &bspDrawSurfaces[ numBSPDrawSurfaces - 1 ];
+                               if( out->numVerts == 3 && out->numIndexes > 3 )
+                               {
+                                       Sys_Printf( "\nWARNING: Potentially bad %s surface (%d: %d, %d)\n     %s\n",
+                                               surfaceTypes[ ds->type ],
+                                               numBSPDrawSurfaces - 1, out->numVerts, out->numIndexes, si->shader );
+                               }
+                       }
+                       
+                       /* ydnar: handle skybox surfaces */
+                       if( ds->skybox )
+                       {
+                               MakeSkyboxSurface( ds );
+                               numSkyboxSurfaces++;
+                       }
+               }
+       }
+       
+       /* emit some statistics */
+       Sys_FPrintf( SYS_VRB, "%9d references\n", numRefs );
+       Sys_FPrintf( SYS_VRB, "%9d (%d) emitted drawsurfs\n", numSurfs, numBSPDrawSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d stripped face surfaces\n", numStripSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d fanned face surfaces\n", numFanSurfaces );
+       Sys_FPrintf( SYS_VRB, "%9d surface models generated\n", numSurfaceModels );
+       Sys_FPrintf( SYS_VRB, "%9d skybox surfaces generated\n", numSkyboxSurfaces );
+       for( i = 0; i < NUM_SURFACE_TYPES; i++ )
+               Sys_FPrintf( SYS_VRB, "%9d %s surfaces\n", numSurfacesByType[ i ], surfaceTypes[ i ] );
+       
+       Sys_FPrintf( SYS_VRB, "%9d redundant indexes supressed, saving %d Kbytes\n", numRedundantIndexes, (numRedundantIndexes * 4 / 1024) );
+}
+
+
+