]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/map.c
Using Sys_FPrintf with SYS_WRN and SYS_ERR
[xonotic/netradiant.git] / tools / quake3 / q3map2 / map.c
index dbc71f1438fbbe391936f6c647dd7188c8d01d1e..22fc166fc3773b8a45bfaf444552982fe9bf45f9 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 MAP_C\r
-\r
-\r
-\r
-/* dependencies */\r
-#include "q3map2.h"\r
-\r
-\r
-\r
-/* FIXME: remove these vars */\r
-\r
-/* undefine to make plane finding use linear sort (note: really slow) */\r
-#define        USE_HASHING\r
-#define        PLANE_HASHES    8192\r
-\r
-plane_t                                        *planehash[ PLANE_HASHES ];\r
-\r
-int                                            c_boxbevels;\r
-int                                            c_edgebevels;\r
-int                                            c_areaportals;\r
-int                                            c_detail;\r
-int                                            c_structural;\r
-\r
-\r
-\r
-/*\r
-PlaneEqual()\r
-ydnar: replaced with variable epsilon for djbob\r
-*/\r
-\r
-#define        NORMAL_EPSILON  0.00001\r
-#define        DIST_EPSILON    0.01\r
-\r
-qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )\r
-{\r
-       float   ne, de;\r
-       \r
-       \r
-       /* get local copies */\r
-       ne = normalEpsilon;\r
-       de = distanceEpsilon;\r
-       \r
-       /* compare */\r
-       if( fabs( p->dist - dist ) <= de &&\r
-               fabs( p->normal[ 0 ] - normal[ 0 ] ) <= ne &&\r
-               fabs( p->normal[ 1 ] - normal[ 1 ] ) <= ne &&\r
-               fabs( p->normal[ 2 ] - normal[ 2 ] ) <= ne )\r
-               return qtrue;\r
-       \r
-       /* different */\r
-       return qfalse;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddPlaneToHash()\r
-*/\r
-\r
-void AddPlaneToHash( plane_t *p )\r
-{\r
-       int             hash;\r
-\r
-       \r
-       hash = (PLANE_HASHES - 1) & (int) fabs( p->dist );\r
-\r
-       p->hash_chain = planehash[hash];\r
-       planehash[hash] = p;\r
-}\r
-\r
-/*\r
-================\r
-CreateNewFloatPlane\r
-================\r
-*/\r
-int CreateNewFloatPlane (vec3_t normal, vec_t dist)\r
-{\r
-       plane_t *p, temp;\r
-\r
-       if (VectorLength(normal) < 0.5)\r
-       {\r
-               Sys_Printf( "FloatPlane: bad normal\n");\r
-               return -1;\r
-       }\r
-\r
-       // create a new plane\r
-       if (nummapplanes+2 > MAX_MAP_PLANES)\r
-               Error ("MAX_MAP_PLANES");\r
-\r
-       p = &mapplanes[nummapplanes];\r
-       VectorCopy (normal, p->normal);\r
-       p->dist = dist;\r
-       p->type = (p+1)->type = PlaneTypeForNormal (p->normal);\r
-\r
-       VectorSubtract (vec3_origin, normal, (p+1)->normal);\r
-       (p+1)->dist = -dist;\r
-\r
-       nummapplanes += 2;\r
-\r
-       // allways put axial planes facing positive first\r
-       if (p->type < 3)\r
-       {\r
-               if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)\r
-               {\r
-                       // flip order\r
-                       temp = *p;\r
-                       *p = *(p+1);\r
-                       *(p+1) = temp;\r
-\r
-                       AddPlaneToHash (p);\r
-                       AddPlaneToHash (p+1);\r
-                       return nummapplanes - 1;\r
-               }\r
-       }\r
-\r
-       AddPlaneToHash (p);\r
-       AddPlaneToHash (p+1);\r
-       return nummapplanes - 2;\r
-}\r
-\r
-\r
-\r
-/*\r
-SnapNormal()\r
-snaps a near-axial normal vector\r
-*/\r
-\r
-void SnapNormal( vec3_t normal )\r
-{\r
-       int             i;\r
-\r
-       for( i = 0; i < 3; i++ )\r
-       {\r
-               if( fabs( normal[ i ] - 1 ) < normalEpsilon )\r
-               {\r
-                       VectorClear( normal );\r
-                       normal[ i ] = 1;\r
-                       break;\r
-               }\r
-               if( fabs( normal[ i ] - -1 ) < normalEpsilon )\r
-               {\r
-                       VectorClear( normal );\r
-                       normal[ i ] = -1;\r
-                       break;\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-SnapPlane()\r
-snaps a plane to normal/distance epsilons\r
-*/\r
-\r
-void SnapPlane( vec3_t normal, vec_t *dist )\r
-{\r
-       SnapNormal( normal );\r
-\r
-       if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )\r
-               *dist = Q_rint( *dist );\r
-}\r
-\r
-\r
-\r
-/*\r
-FindFloatPlane()\r
-ydnar: changed to allow a number of test points to be supplied that\r
-must be within an epsilon distance of the plane\r
-*/\r
-\r
-int FindFloatPlane( vec3_t normal, vec_t dist, int numPoints, vec3_t *points )\r
-\r
-#ifdef USE_HASHING\r
-\r
-{\r
-       int             i, j, hash, h;\r
-       plane_t *p;\r
-       vec_t   d;\r
-       \r
-       \r
-       /* hash the plane */\r
-       SnapPlane( normal, &dist );\r
-       hash = (PLANE_HASHES - 1) & (int) fabs( dist );\r
-       \r
-       /* search the border bins as well */\r
-       for( i = -1; i <= 1; i++ )\r
-       {\r
-               h = (hash + i) & (PLANE_HASHES - 1);\r
-               for( p = planehash[ h ]; p != NULL; p = p->hash_chain )\r
-               {\r
-                       /* do standard plane compare */\r
-                       if( !PlaneEqual( p, normal, dist ) )\r
-                               continue;\r
-                       \r
-                       /* ydnar: uncomment the following line for old-style plane finding */\r
-                       //%     return p - mapplanes;\r
-                       \r
-                       /* ydnar: test supplied points against this plane */\r
-                       for( j = 0; j < numPoints; j++ )\r
-                       {\r
-                               d = DotProduct( points[ j ], normal ) - dist;\r
-                               if( fabs( d ) > distanceEpsilon )\r
-                                       break;\r
-                       }\r
-                       \r
-                       /* found a matching plane */\r
-                       if( j >= numPoints )\r
-                               return p - mapplanes;\r
-               }\r
-       }\r
-       \r
-       /* none found, so create a new one */\r
-       return CreateNewFloatPlane( normal, dist );\r
-}\r
-\r
-#else\r
-\r
-{\r
-       int             i;\r
-       plane_t *p;\r
-       \r
-\r
-       SnapPlane( normal, &dist );\r
-       for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )\r
-       {\r
-               if( PlaneEqual( p, normal, dist ) )\r
-                       return i;\r
-       }\r
-       \r
-       return CreateNewFloatPlane( normal, dist );\r
-}\r
-\r
-#endif\r
-\r
-\r
-\r
-/*\r
-MapPlaneFromPoints()\r
-takes 3 points and finds the plane they lie in\r
-*/\r
-\r
-int MapPlaneFromPoints( vec3_t *p )\r
-{\r
-       vec3_t  t1, t2, normal;\r
-       vec_t   dist;\r
-       \r
-       \r
-       /* calc plane normal */\r
-       VectorSubtract( p[ 0 ], p[ 1 ], t1 );\r
-       VectorSubtract( p[ 2 ], p[ 1 ], t2 );\r
-       CrossProduct( t1, t2, normal );\r
-       VectorNormalize( normal, normal );\r
-       \r
-       /* calc plane distance */\r
-       dist = DotProduct( p[ 0 ], normal );\r
-       \r
-       /* store the plane */\r
-       return FindFloatPlane( normal, dist, 3, p );\r
-}\r
-\r
-\r
-\r
-/*\r
-SetBrushContents()\r
-the content flags and compile flags on all sides of a brush should be the same\r
-*/\r
-\r
-void SetBrushContents( brush_t *b )\r
-{\r
-       int                     contentFlags, compileFlags;\r
-       side_t          *s;\r
-       int                     i;\r
-       qboolean        mixed;\r
-       \r
-       \r
-       /* get initial compile flags from first side */\r
-       s = &b->sides[ 0 ];\r
-       contentFlags = s->contentFlags;\r
-       compileFlags = s->compileFlags;\r
-       b->contentShader = s->shaderInfo;\r
-       mixed = qfalse;\r
-       \r
-       /* get the content/compile flags for every side in the brush */\r
-       for( i = 1; i < b->numsides; i++, s++ )\r
-       {\r
-               s = &b->sides[ i ];\r
-               if( s->shaderInfo == NULL )\r
-                       continue;\r
-               if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )\r
-                       mixed = qtrue;\r
-       }\r
-       \r
-       /* ydnar: getting rid of this stupid warning */\r
-       //%     if( mixed )\r
-       //%             Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );\r
-\r
-       /* check for detail & structural */\r
-       if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )\r
-       {\r
-               xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );\r
-               compileFlags &= ~C_DETAIL;\r
-       }\r
-       \r
-       /* the fulldetail flag will cause detail brushes to be treated like normal brushes */\r
-       if( fulldetail )\r
-               compileFlags &= ~C_DETAIL;\r
-       \r
-       /* all translucent brushes that aren't specifically made structural will be detail */\r
-       if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )\r
-               compileFlags |= C_DETAIL;\r
-       \r
-       /* detail? */\r
-       if( compileFlags & C_DETAIL )\r
-       {\r
-               c_detail++;\r
-               b->detail = qtrue;\r
-       }\r
-       else\r
-       {\r
-               c_structural++;\r
-               b->detail = qfalse;\r
-       }\r
-       \r
-       /* opaque? */\r
-       if( compileFlags & C_TRANSLUCENT )\r
-               b->opaque = qfalse;\r
-       else\r
-               b->opaque = qtrue;\r
-       \r
-       /* areaportal? */\r
-       if( compileFlags & C_AREAPORTAL )\r
-               c_areaportals++;\r
-       \r
-       /* set brush flags */\r
-       b->contentFlags = contentFlags;\r
-       b->compileFlags = compileFlags;\r
-}\r
-\r
-\r
-\r
-/*\r
-AddBrushBevels()\r
-adds any additional planes necessary to allow the brush being\r
-built to be expanded against axial bounding boxes\r
-ydnar 2003-01-20: added mrelusive fixes\r
-*/\r
-\r
-void AddBrushBevels( void )\r
-{\r
-       int                     axis, dir;\r
-       int                     i, j, k, l, order;\r
-       side_t          sidetemp;\r
-       side_t          *s, *s2;\r
-       winding_t       *w, *w2;\r
-       vec3_t          normal;\r
-       float           dist;\r
-       vec3_t          vec, vec2;\r
-       float           d, minBack;\r
-\r
-       //\r
-       // add the axial planes\r
-       //\r
-       order = 0;\r
-       for ( axis = 0; axis < 3; axis++ ) {\r
-               for ( dir = -1; dir <= 1; dir += 2, order++ ) {\r
-                       // see if the plane is allready present\r
-                       for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )\r
-                       {\r
-                               /* ydnar: testing disabling of mre code */\r
-                               #if 0\r
-                                       if ( dir > 0 ) {\r
-                                               if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-                                       else {\r
-                                               if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-                               #else\r
-                                       if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||\r
-                                               (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )\r
-                                               break;\r
-                               #endif\r
-                       }\r
-\r
-                       if ( i == buildBrush->numsides ) {\r
-                               // add a new side\r
-                               if ( buildBrush->numsides == MAX_BUILD_SIDES ) {\r
-                                       xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);\r
-                               }\r
-                               memset( s, 0, sizeof( *s ) );\r
-                               buildBrush->numsides++;\r
-                               VectorClear (normal);\r
-                               normal[axis] = dir;\r
-\r
-                               if( dir == 1 )\r
-                               {\r
-                                       /* ydnar: adding bevel plane snapping for fewer bsp planes */\r
-                                       if( bevelSnap > 0 )\r
-                                               dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;\r
-                                       else\r
-                                               dist = buildBrush->maxs[ axis ];\r
-                               }\r
-                               else\r
-                               {\r
-                                       /* ydnar: adding bevel plane snapping for fewer bsp planes */\r
-                                       if( bevelSnap > 0 )\r
-                                               dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;\r
-                                       else\r
-                                               dist = -buildBrush->mins[ axis ];\r
-                               }\r
-\r
-                               s->planenum = FindFloatPlane( normal, dist, 0, NULL );\r
-                               s->contentFlags = buildBrush->sides[ 0 ].contentFlags;\r
-                               s->bevel = qtrue;\r
-                               c_boxbevels++;\r
-                       }\r
-\r
-                       // if the plane is not in it canonical order, swap it\r
-                       if ( i != order ) {\r
-                               sidetemp = buildBrush->sides[order];\r
-                               buildBrush->sides[order] = buildBrush->sides[i];\r
-                               buildBrush->sides[i] = sidetemp;\r
-                       }\r
-               }\r
-       }\r
-\r
-       //\r
-       // add the edge bevels\r
-       //\r
-       if ( buildBrush->numsides == 6 ) {\r
-               return;         // pure axial\r
-       }\r
-\r
-       // test the non-axial plane edges\r
-       for ( i = 6; i < buildBrush->numsides; i++ ) {\r
-               s = buildBrush->sides + i;\r
-               w = s->winding;\r
-               if ( !w ) {\r
-                       continue;\r
-               }\r
-               for ( j = 0; j < w->numpoints; j++) {\r
-                       k = (j+1)%w->numpoints;\r
-                       VectorSubtract( w->p[j], w->p[k], vec );\r
-                       if ( VectorNormalize( vec, vec ) < 0.5f ) {\r
-                               continue;\r
-                       }\r
-                       SnapNormal( vec );\r
-                       for ( k = 0; k < 3; k++ ) {\r
-                               if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {\r
-                                       break;  // axial\r
-                               }\r
-                       }\r
-                       if ( k != 3 ) {\r
-                               continue;       // only test non-axial edges\r
-                       }\r
-\r
-                       /* debug code */\r
-                       //%     Sys_Printf( "-------------\n" );\r
-\r
-                       // try the six possible slanted axials from this edge\r
-                       for ( axis = 0; axis < 3; axis++ ) {\r
-                               for ( dir = -1; dir <= 1; dir += 2 ) {\r
-                                       // construct a plane\r
-                                       VectorClear( vec2 );\r
-                                       vec2[axis] = dir;\r
-                                       CrossProduct( vec, vec2, normal );\r
-                                       if ( VectorNormalize( normal, normal ) < 0.5f ) {\r
-                                               continue;\r
-                                       }\r
-                                       dist = DotProduct( w->p[j], normal );\r
-                                       \r
-                                       // if all the points on all the sides are\r
-                                       // behind this plane, it is a proper edge bevel\r
-                                       for ( k = 0; k < buildBrush->numsides; k++ ) {\r
-\r
-                                               // if this plane has allready been used, skip it\r
-                                               if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {\r
-                                                       break;\r
-                                               }\r
-\r
-                                               w2 = buildBrush->sides[k].winding;\r
-                                               if ( !w2 ) {\r
-                                                       continue;\r
-                                               }\r
-                                               minBack = 0.0f;\r
-                                               for ( l = 0; l < w2->numpoints; l++ ) {\r
-                                                       d = DotProduct( w2->p[l], normal ) - dist;\r
-                                                       if ( d > 0.1f ) {\r
-                                                               break;  // point in front\r
-                                                       }\r
-                                                       if ( d < minBack ) {\r
-                                                               minBack = d;\r
-                                                       }\r
-                                               }\r
-                                               // if some point was at the front\r
-                                               if ( l != w2->numpoints ) {\r
-                                                       break;\r
-                                               }\r
-\r
-                                               // if no points at the back then the winding is on the bevel plane\r
-                                               if ( minBack > -0.1f ) {\r
-                                                       //%     Sys_Printf( "On bevel plane\n" );\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-\r
-                                       if ( k != buildBrush->numsides ) {\r
-                                               continue;       // wasn't part of the outer hull\r
-                                       }\r
-                                       \r
-                                       /* debug code */\r
-                                       //%     Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );\r
-                                       \r
-                                       // add this plane\r
-                                       if( buildBrush->numsides == MAX_BUILD_SIDES ) {\r
-                                               xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);\r
-                                       }\r
-                                       s2 = &buildBrush->sides[buildBrush->numsides];\r
-                                       buildBrush->numsides++;\r
-                                       memset( s2, 0, sizeof( *s2 ) );\r
-\r
-                                       s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );\r
-                                       s2->contentFlags = buildBrush->sides[0].contentFlags;\r
-                                       s2->bevel = qtrue;\r
-                                       c_edgebevels++;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-FinishBrush()\r
-produces a final brush based on the buildBrush->sides array\r
-and links it to the current entity\r
-*/\r
-\r
-brush_t *FinishBrush( void )\r
-{\r
-       brush_t *b;\r
-       \r
-       \r
-       /* create windings for sides and bounds for brush */\r
-       if ( !CreateBrushWindings( buildBrush ) )\r
-               return NULL;\r
-\r
-       /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.\r
-          after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */\r
-       if( buildBrush->compileFlags & C_ORIGIN )\r
-       {\r
-               char    string[ 32 ];\r
-               vec3_t  origin;\r
-\r
-               if( numEntities == 1 )\r
-               {\r
-                       Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n", \r
-                               mapEnt->mapEntityNum, entitySourceBrushes );\r
-                       return NULL;\r
-               }\r
-               \r
-               VectorAdd (buildBrush->mins, buildBrush->maxs, origin);\r
-               VectorScale (origin, 0.5, origin);\r
-\r
-               sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );\r
-               SetKeyValue( &entities[ numEntities - 1 ], "origin", string);\r
-\r
-               VectorCopy( origin, entities[ numEntities - 1 ].origin);\r
-\r
-               /* don't keep this brush */\r
-               return NULL;\r
-       }\r
-       \r
-       /* determine if the brush is an area portal */\r
-       if( buildBrush->compileFlags & C_AREAPORTAL )\r
-       {\r
-               if( numEntities != 1 )\r
-               {\r
-                       Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );\r
-                       return NULL;\r
-               }\r
-       }\r
-       \r
-       /* add bevel planes */\r
-       AddBrushBevels();\r
-       \r
-       /* keep it */\r
-       b = CopyBrush( buildBrush );\r
-       \r
-       /* set map entity and brush numbering */\r
-       b->entityNum = mapEnt->mapEntityNum;\r
-       b->brushNum = entitySourceBrushes;\r
-       \r
-       /* set original */\r
-       b->original = b;\r
-       \r
-       /* link opaque brushes to head of list, translucent brushes to end */\r
-       if( b->opaque || mapEnt->lastBrush == NULL )\r
-       {\r
-               b->next = mapEnt->brushes;\r
-               mapEnt->brushes = b;\r
-               if( mapEnt->lastBrush == NULL )\r
-                       mapEnt->lastBrush = b;\r
-       }\r
-       else\r
-       {\r
-               b->next = NULL;\r
-               mapEnt->lastBrush->next = b;\r
-               mapEnt->lastBrush = b;\r
-       }\r
-       \r
-       /* return to sender */\r
-       return b;\r
-}\r
-\r
-\r
-\r
-/*\r
-TextureAxisFromPlane()\r
-determines best orthagonal axis to project a texture onto a wall\r
-(must be identical in radiant!)\r
-*/\r
-\r
-vec3_t baseaxis[18] =\r
-{\r
-       {0,0,1}, {1,0,0}, {0,-1,0},                     // floor\r
-       {0,0,-1}, {1,0,0}, {0,-1,0},            // ceiling\r
-       {1,0,0}, {0,1,0}, {0,0,-1},                     // west wall\r
-       {-1,0,0}, {0,1,0}, {0,0,-1},            // east wall\r
-       {0,1,0}, {1,0,0}, {0,0,-1},                     // south wall\r
-       {0,-1,0}, {1,0,0}, {0,0,-1}                     // north wall\r
-};\r
-\r
-void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )\r
-{\r
-       int             bestaxis;\r
-       vec_t   dot,best;\r
-       int             i;\r
-       \r
-       best = 0;\r
-       bestaxis = 0;\r
-       \r
-       for (i=0 ; i<6 ; i++)\r
-       {\r
-               dot = DotProduct (pln->normal, baseaxis[i*3]);\r
-               if( dot > best + 0.0001f )      /* ydnar: bug 637 fix, suggested by jmonroe */\r
-               {\r
-                       best = dot;\r
-                       bestaxis = i;\r
-               }\r
-       }\r
-       \r
-       VectorCopy (baseaxis[bestaxis*3+1], xv);\r
-       VectorCopy (baseaxis[bestaxis*3+2], yv);\r
-}\r
-\r
-\r
-\r
-/*\r
-QuakeTextureVecs()\r
-creates world-to-texture mapping vecs for crappy quake plane arrangements\r
-*/\r
-\r
-void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )\r
-{\r
-       vec3_t  vecs[2];\r
-       int             sv, tv;\r
-       vec_t   ang, sinv, cosv;\r
-       vec_t   ns, nt;\r
-       int             i, j;\r
-       \r
-       \r
-       TextureAxisFromPlane(plane, vecs[0], vecs[1]);\r
-       \r
-       if (!scale[0])\r
-               scale[0] = 1;\r
-       if (!scale[1])\r
-               scale[1] = 1;\r
-\r
-       // rotate axis\r
-       if (rotate == 0)\r
-               { sinv = 0 ; cosv = 1; }\r
-       else if (rotate == 90)\r
-               { sinv = 1 ; cosv = 0; }\r
-       else if (rotate == 180)\r
-               { sinv = 0 ; cosv = -1; }\r
-       else if (rotate == 270)\r
-               { sinv = -1 ; cosv = 0; }\r
-       else\r
-       {       \r
-               ang = rotate / 180 * Q_PI;\r
-               sinv = sin(ang);\r
-               cosv = cos(ang);\r
-       }\r
-\r
-       if (vecs[0][0])\r
-               sv = 0;\r
-       else if (vecs[0][1])\r
-               sv = 1;\r
-       else\r
-               sv = 2;\r
-                               \r
-       if (vecs[1][0])\r
-               tv = 0;\r
-       else if (vecs[1][1])\r
-               tv = 1;\r
-       else\r
-               tv = 2;\r
-                                       \r
-       for (i=0 ; i<2 ; i++) {\r
-               ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];\r
-               nt = sinv * vecs[i][sv] +  cosv * vecs[i][tv];\r
-               vecs[i][sv] = ns;\r
-               vecs[i][tv] = nt;\r
-       }\r
-\r
-       for (i=0 ; i<2 ; i++)\r
-               for (j=0 ; j<3 ; j++)\r
-                       mappingVecs[i][j] = vecs[i][j] / scale[i];\r
-\r
-       mappingVecs[0][3] = shift[0];\r
-       mappingVecs[1][3] = shift[1];\r
-}\r
-\r
-\r
-\r
-/*\r
-ParseRawBrush()\r
-parses the sides into buildBrush->sides[], nothing else.\r
-no validation, back plane removal, etc.\r
-\r
-Timo - 08/26/99\r
-added brush epairs parsing ( ignoring actually )\r
-Timo - 08/04/99\r
-added exclusive brush primitive parsing\r
-Timo - 08/08/99\r
-support for old brush format back in\r
-NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes\r
-*/\r
-\r
-static void ParseRawBrush( qboolean onlyLights )\r
-{\r
-       side_t                  *side;\r
-       vec3_t                  planePoints[ 3 ];\r
-       int                             planenum;\r
-       shaderInfo_t    *si;\r
-       vec_t                   shift[ 2 ];\r
-       vec_t                   rotate;\r
-       vec_t                   scale[ 2 ];\r
-       char                    name[ MAX_QPATH ];\r
-       char                    shader[ MAX_QPATH ];\r
-       int                             flags;\r
-       \r
-       \r
-       /* initial setup */\r
-       buildBrush->numsides = 0;\r
-       buildBrush->detail = qfalse;\r
-       \r
-       /* bp */\r
-       if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
-               MatchToken( "{" );\r
-       \r
-       /* parse sides */\r
-       while( 1 )\r
-       {\r
-               if( !GetToken( qtrue ) )\r
-                       break;\r
-               if( !strcmp( token, "}" ) )\r
-                       break;\r
-                \r
-               /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */\r
-               if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
-               {\r
-                       while( 1 )\r
-                       {\r
-                               if( strcmp( token, "(" ) )\r
-                                       GetToken( qfalse );\r
-                               else\r
-                                       break;\r
-                               GetToken( qtrue );\r
-                       }\r
-               }\r
-               UnGetToken();\r
-               \r
-               /* test side count */\r
-               if( buildBrush->numsides >= MAX_BUILD_SIDES )\r
-                       xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );\r
-               \r
-               /* add side */\r
-               side = &buildBrush->sides[ buildBrush->numsides ];\r
-               memset( side, 0, sizeof( *side ) );\r
-               buildBrush->numsides++;\r
-               \r
-               /* read the three point plane definition */\r
-               Parse1DMatrix( 3, planePoints[ 0 ] );\r
-               Parse1DMatrix( 3, planePoints[ 1 ] );\r
-               Parse1DMatrix( 3, planePoints[ 2 ] );\r
-               \r
-               /* bp: read the texture matrix */\r
-               if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
-                       Parse2DMatrix( 2, 3, (float*) side->texMat );\r
-               \r
-               /* read shader name */\r
-               GetToken( qfalse );\r
-               strcpy( name, token );\r
-               \r
-               /* bp */\r
-               if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
-               {\r
-                       GetToken( qfalse );\r
-                       shift[ 0 ] = atof( token );\r
-                       GetToken( qfalse );\r
-                       shift[ 1 ] = atof( token );\r
-                       GetToken( qfalse );\r
-                       rotate = atof( token ); \r
-                       GetToken( qfalse );\r
-                       scale[ 0 ] = atof( token );\r
-                       GetToken( qfalse );\r
-                       scale[ 1 ] = atof( token );\r
-               }\r
-               \r
-               /* set default flags and values */\r
-               sprintf( shader, "textures/%s", name );\r
-               if( onlyLights )\r
-                       si = &shaderInfo[ 0 ];\r
-               else\r
-                       si = ShaderInfoForShader( shader );\r
-               side->shaderInfo = si;\r
-               side->surfaceFlags = si->surfaceFlags;\r
-               side->contentFlags = si->contentFlags;\r
-               side->compileFlags = si->compileFlags;\r
-               side->value = si->value;\r
-               \r
-               /* ydnar: gs mods: bias texture shift */\r
-               if( si->globalTexture == qfalse )\r
-               {\r
-                       shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);\r
-                       shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);\r
-               }\r
-               \r
-               /*\r
-                       historically, there are 3 integer values at the end of a brushside line in a .map file.\r
-                       in quake 3, the only thing that mattered was the first of these three values, which\r
-                       was previously the content flags. and only then did a single bit matter, the detail\r
-                       bit. because every game has its own special flags for specifying detail, the\r
-                       traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0\r
-                       by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but\r
-                       is stored in compileFlags, as opposed to contentFlags, for multiple-game\r
-                       portability. :sigh:\r
-               */\r
-               \r
-               if( TokenAvailable() )\r
-               {\r
-                       /* get detail bit from map content flags */\r
-                       GetToken( qfalse );\r
-                       flags = atoi( token );\r
-                       if( flags & C_DETAIL )\r
-                               side->compileFlags |= C_DETAIL;\r
-                       \r
-                       /* historical */\r
-                       GetToken( qfalse );\r
-                       //% td.flags = atoi( token );\r
-                       GetToken( qfalse );\r
-                       //% td.value = atoi( token );\r
-               }\r
-               \r
-               /* find the plane number */\r
-               planenum = MapPlaneFromPoints( planePoints );\r
-               side->planenum = planenum;\r
-               \r
-               /* bp: get the texture mapping for this texturedef / plane combination */\r
-               if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
-                       QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );\r
-       }\r
-       \r
-       /* bp */\r
-       if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
-       {\r
-               UnGetToken();\r
-               MatchToken( "}" );\r
-               MatchToken( "}" );\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-RemoveDuplicateBrushPlanes\r
-returns false if the brush has a mirrored set of planes,\r
-meaning it encloses no volume.\r
-also removes planes without any normal\r
-*/\r
-\r
-qboolean RemoveDuplicateBrushPlanes( brush_t *b )\r
-{\r
-       int                     i, j, k;\r
-       side_t          *sides;\r
-\r
-       sides = b->sides;\r
-\r
-       for ( i = 1 ; i < b->numsides ; i++ ) {\r
-\r
-               // check for a degenerate plane\r
-               if ( sides[i].planenum == -1) {\r
-                 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );\r
-                       // remove it\r
-                       for ( k = i + 1 ; k < b->numsides ; k++ ) {\r
-                               sides[k-1] = sides[k];\r
-                       }\r
-                       b->numsides--;\r
-                       i--;\r
-                       continue;\r
-               }\r
-\r
-               // check for duplication and mirroring\r
-               for ( j = 0 ; j < i ; j++ ) {\r
-                       if ( sides[i].planenum == sides[j].planenum ) {\r
-                         xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );\r
-                               // remove the second duplicate\r
-                               for ( k = i + 1 ; k < b->numsides ; k++ ) {\r
-                                       sides[k-1] = sides[k];\r
-                               }\r
-                               b->numsides--;\r
-                               i--;\r
-                               break;\r
-                       }\r
-\r
-                       if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {\r
-                               // mirror plane, brush is invalid\r
-                         xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );\r
-                               return qfalse;\r
-                       }\r
-               }\r
-       }\r
-       return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-ParseBrush()\r
-parses a brush out of a map file and sets it up\r
-*/\r
-\r
-static void ParseBrush( qboolean onlyLights )\r
-{\r
-       brush_t *b;\r
-       \r
-       \r
-       /* parse the brush out of the map */\r
-       ParseRawBrush( onlyLights );\r
-       \r
-       /* only go this far? */\r
-       if( onlyLights )\r
-               return;\r
-       \r
-       /* set some defaults */\r
-       buildBrush->portalareas[ 0 ] = -1;\r
-       buildBrush->portalareas[ 1 ] = -1;\r
-       buildBrush->entityNum = numMapEntities - 1;\r
-       buildBrush->brushNum = entitySourceBrushes;\r
-       \r
-       /* if there are mirrored planes, the entire brush is invalid */\r
-       if( !RemoveDuplicateBrushPlanes( buildBrush ) )\r
-               return;\r
-       \r
-       /* get the content for the entire brush */\r
-       SetBrushContents( buildBrush );\r
-       \r
-       /* allow detail brushes to be removed */\r
-       if( nodetail && (buildBrush->compileFlags & C_DETAIL) )\r
-       {\r
-               //%     FreeBrush( buildBrush );\r
-               return;\r
-       }\r
-       \r
-       /* allow liquid brushes to be removed */\r
-       if( nowater && (buildBrush->compileFlags & C_LIQUID ) )\r
-       {\r
-               //%     FreeBrush( buildBrush );\r
-               return;\r
-       }\r
-       \r
-       /* ydnar: allow hint brushes to be removed */\r
-       if( noHint && (buildBrush->compileFlags & C_HINT) )\r
-       {\r
-               //%     FreeBrush( buildBrush );\r
-               return;\r
-       }\r
-       \r
-       /* finish the brush */\r
-       b = FinishBrush();\r
-}\r
-\r
-\r
-\r
-/*\r
-MoveBrushesToWorld()\r
-takes all of the brushes from the current entity and\r
-adds them to the world's brush list\r
-(used by func_group)\r
-*/\r
-\r
-void MoveBrushesToWorld( entity_t *ent )\r
-{\r
-       brush_t         *b, *next;\r
-       parseMesh_t     *pm;\r
-\r
-       \r
-       /* move brushes */\r
-       for( b = ent->brushes; b != NULL; b = next )\r
-       {\r
-               /* get next brush */\r
-               next = b->next;\r
-               \r
-               /* link opaque brushes to head of list, translucent brushes to end */\r
-               if( b->opaque || entities[ 0 ].lastBrush == NULL )\r
-               {\r
-                       b->next = entities[ 0 ].brushes;\r
-                       entities[ 0 ].brushes = b;\r
-                       if( entities[ 0 ].lastBrush == NULL )\r
-                               entities[ 0 ].lastBrush = b;\r
-               }\r
-               else\r
-               {\r
-                       b->next = NULL;\r
-                       entities[ 0 ].lastBrush->next = b;\r
-                       entities[ 0 ].lastBrush = b;\r
-               }\r
-               \r
-               //%     b->next = entities[ 0 ].brushes;\r
-               //%     entities[ 0 ].brushes = b;\r
-       }\r
-       ent->brushes = NULL;\r
-       \r
-       /* move patches */\r
-       if( ent->patches != NULL )\r
-       {\r
-               for( pm = ent->patches; pm->next; pm = pm->next );\r
-               \r
-               pm->next = entities[ 0 ].patches;\r
-               entities[ 0 ].patches = ent->patches;\r
-               \r
-               ent->patches = NULL;\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-AdjustBrushesForOrigin()\r
-*/\r
-\r
-void AdjustBrushesForOrigin( entity_t *ent )\r
-{\r
-       \r
-       int                     i;\r
-       side_t          *s;\r
-       vec_t           newdist;\r
-       brush_t         *b;\r
-       parseMesh_t     *p;\r
-       \r
-       \r
-       /* walk brush list */\r
-       for( b = ent->brushes; b != NULL; b = b->next )\r
-       {\r
-               /* offset brush planes */\r
-               for( i = 0; i < b->numsides; i++)\r
-               {\r
-                       /* get brush side */\r
-                       s = &b->sides[ i ];\r
-                       \r
-                       /* offset side plane */\r
-                       newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin );\r
-                       \r
-                       /* find a new plane */\r
-                       s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );\r
-               }\r
-               \r
-               /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */\r
-               CreateBrushWindings( b );\r
-       }\r
-       \r
-       /* walk patch list */\r
-       for( p = ent->patches; p != NULL; p = p->next )\r
-       {\r
-               for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )\r
-                       VectorSubtract( p->mesh.verts[ i ].xyz, ent->origin, p->mesh.verts[ i ].xyz );\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-SetEntityBounds() - ydnar\r
-finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)\r
-*/\r
-\r
-void SetEntityBounds( entity_t *e )\r
-{\r
-       int                     i;\r
-       brush_t *b;\r
-       parseMesh_t     *p;\r
-       vec3_t          mins, maxs;\r
-       const char      *value;\r
-\r
-       \r
-       \r
-\r
-       /* walk the entity's brushes/patches and determine bounds */\r
-       ClearBounds( mins, maxs );\r
-       for( b = e->brushes; b; b = b->next )\r
-       {\r
-               AddPointToBounds( b->mins, mins, maxs );\r
-               AddPointToBounds( b->maxs, mins, maxs );\r
-       }\r
-       for( p = e->patches; p; p = p->next )\r
-       {\r
-               for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )\r
-                       AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );\r
-       }\r
-       \r
-       /* try to find explicit min/max key */\r
-       value = ValueForKey( e, "min" ); \r
-       if( value[ 0 ] != '\0' )\r
-               GetVectorForKey( e, "min", mins );\r
-       value = ValueForKey( e, "max" ); \r
-       if( value[ 0 ] != '\0' )\r
-               GetVectorForKey( e, "max", maxs );\r
-       \r
-       /* store the bounds */\r
-       for( b = e->brushes; b; b = b->next )\r
-       {\r
-               VectorCopy( mins, b->eMins );\r
-               VectorCopy( maxs, b->eMaxs );\r
-       }\r
-       for( p = e->patches; p; p = p->next )\r
-       {\r
-               VectorCopy( mins, p->eMins );\r
-               VectorCopy( maxs, p->eMaxs );\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-LoadEntityIndexMap() - ydnar\r
-based on LoadAlphaMap() from terrain.c, a little more generic\r
-*/\r
-\r
-void LoadEntityIndexMap( entity_t *e )\r
-{\r
-       int                             i, size, numLayers, w, h;\r
-       const char              *value, *indexMapFilename, *shader;\r
-       char                    ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;\r
-       byte                    *pixels;\r
-       unsigned int    *pixels32;\r
-       indexMap_t              *im;\r
-       brush_t                 *b;\r
-       parseMesh_t             *p;\r
-       \r
-       \r
-       /* this only works with bmodel ents */\r
-       if( e->brushes == NULL && e->patches == NULL )\r
-               return;\r
-       \r
-       /* determine if there is an index map (support legacy "alphamap" key as well) */\r
-       value = ValueForKey( e, "_indexmap" );\r
-       if( value[ 0 ] == '\0' )\r
-               value = ValueForKey( e, "alphamap" );\r
-       if( value[ 0 ] == '\0' )\r
-               return;\r
-       indexMapFilename = value;\r
-       \r
-       /* get number of layers (support legacy "layers" key as well) */\r
-       value = ValueForKey( e, "_layers" );\r
-       if( value[ 0 ] == '\0' )\r
-               value = ValueForKey( e, "layers" );\r
-       if( value[ 0 ] == '\0' )\r
-       {\r
-               Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );\r
-               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
-               return;\r
-       }\r
-       numLayers = atoi( value );\r
-       if( numLayers < 1 )\r
-       {\r
-               Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );\r
-               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
-               return;\r
-       }\r
-       \r
-       /* get base shader name (support legacy "shader" key as well) */\r
-       value = ValueForKey( mapEnt, "_shader" );\r
-       if( value[ 0 ] == '\0' )\r
-               value = ValueForKey( e, "shader" );\r
-       if( value[ 0 ] == '\0' )\r
-       {\r
-               Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );\r
-               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
-               return;\r
-       }\r
-       shader = value;\r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n",  mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );\r
-       \r
-       /* get index map file extension */\r
-       ExtractFileExtension( indexMapFilename, ext );\r
-       \r
-       /* handle tga image */\r
-       if( !Q_stricmp( ext, "tga" ) )\r
-       {\r
-               /* load it */\r
-               Load32BitImage( indexMapFilename, &pixels32, &w, &h );\r
-               \r
-               /* convert to bytes */\r
-               size = w * h;\r
-               pixels = safe_malloc( size );\r
-               for( i = 0; i < size; i++ )\r
-               {\r
-                       pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;\r
-                       if( pixels[ i ] >= numLayers )\r
-                               pixels[ i ] = numLayers - 1;\r
-               }\r
-               \r
-               /* free the 32 bit image */\r
-               free( pixels32 );\r
-       }\r
-       else\r
-       {\r
-               /* load it */\r
-               Load256Image( indexMapFilename, &pixels, NULL, &w, &h );\r
-               \r
-               /* debug code */\r
-               //%     Sys_Printf( "-------------------------------" );\r
-               \r
-               /* fix up out-of-range values */\r
-               size = w * h;\r
-               for( i = 0; i < size; i++ )\r
-               {\r
-                       if( pixels[ i ] >= numLayers )\r
-                               pixels[ i ] = numLayers - 1;\r
-                       \r
-                       /* debug code */\r
-                       //%     if( (i % w) == 0 )\r
-                       //%             Sys_Printf( "\n" );\r
-                       //%     Sys_Printf( "%c", pixels[ i ] + '0' );\r
-               }\r
-               \r
-               /* debug code */\r
-               //%     Sys_Printf( "\n-------------------------------\n" );\r
-       }\r
-       \r
-       /* the index map must be at least 2x2 pixels */\r
-       if( w < 2 || h < 2 )\r
-       {\r
-               Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );\r
-               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );\r
-               free( pixels );\r
-               return;\r
-       }\r
-\r
-       /* create a new index map */\r
-       im = safe_malloc( sizeof( *im ) );\r
-       memset( im, 0, sizeof( *im ) );\r
-       \r
-       /* set it up */\r
-       im->w = w;\r
-       im->h = h;\r
-       im->numLayers = numLayers;\r
-       strcpy( im->name, indexMapFilename );\r
-       strcpy( im->shader, shader );\r
-       im->pixels = pixels;\r
-       \r
-       /* get height offsets */\r
-       value = ValueForKey( mapEnt, "_offsets" );\r
-       if( value[ 0 ] == '\0' )\r
-               value = ValueForKey( e, "offsets" );\r
-       if( value[ 0 ] != '\0' )\r
-       {\r
-               /* value is a space-seperated set of numbers */\r
-               strcpy( offset, value );\r
-               search = offset;\r
-               \r
-               /* get each value */\r
-               for( i = 0; i < 256 && *search != '\0'; i++ )\r
-               {\r
-                       space = strstr( search, " " );\r
-                       if( space != NULL )\r
-                               *space = '\0';\r
-                       im->offsets[ i ] = atof( search );\r
-                       if( space == NULL )\r
-                               break;\r
-                       search = space + 1;\r
-               }\r
-       }\r
-       \r
-       /* store the index map in every brush/patch in the entity */\r
-       for( b = e->brushes; b != NULL; b = b->next )\r
-               b->im = im;\r
-       for( p = e->patches; p != NULL; p = p->next )\r
-               p->im = im;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-/*\r
-ParseMapEntity()\r
-parses a single entity out of a map file\r
-*/\r
-\r
-static qboolean ParseMapEntity( qboolean onlyLights )\r
-{\r
-       epair_t                 *ep;\r
-       const char              *classname, *value;\r
-       float                   lightmapScale;\r
-       char                    shader[ MAX_QPATH ];\r
-       shaderInfo_t    *celShader = NULL;\r
-       brush_t                 *brush;\r
-       parseMesh_t             *patch;\r
-       qboolean                funcGroup;\r
-       int                             castShadows, recvShadows;\r
-       \r
-       \r
-       /* eof check */\r
-       if( !GetToken( qtrue ) )\r
-               return qfalse;\r
-       \r
-       /* conformance check */\r
-       if( strcmp( token, "{" ) )\r
-       {\r
-               Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"\r
-                       "Continuing to process map, but resulting BSP may be invalid.\n",\r
-                       token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );\r
-               return qfalse;\r
-       }\r
-       \r
-       /* range check */\r
-       if( numEntities >= MAX_MAP_ENTITIES )\r
-               Error( "numEntities == MAX_MAP_ENTITIES" );\r
-       \r
-       /* setup */\r
-       entitySourceBrushes = 0;\r
-       mapEnt = &entities[ numEntities ];\r
-       numEntities++;\r
-       memset( mapEnt, 0, sizeof( *mapEnt ) );\r
-       \r
-       /* ydnar: true entity numbering */\r
-       mapEnt->mapEntityNum = numMapEntities;\r
-       numMapEntities++;\r
-       \r
-       /* loop */\r
-       while( 1 )\r
-       {\r
-               /* get initial token */\r
-               if( !GetToken( qtrue ) )\r
-               {\r
-                       Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"\r
-                               "Continuing to process map, but resulting BSP may be invalid.\n" );\r
-                       return qfalse;\r
-               }\r
-               \r
-               if( !strcmp( token, "}" ) )\r
-                       break;\r
-               \r
-               if( !strcmp( token, "{" ) )\r
-               {\r
-                       /* parse a brush or patch */\r
-                       if( !GetToken( qtrue ) )\r
-                               break;\r
-                       \r
-                       /* check */\r
-                       if( !strcmp( token, "patchDef2" ) )\r
-                       {\r
-                               numMapPatches++;\r
-                               ParsePatch( onlyLights );\r
-                       }\r
-                       else if( !strcmp( token, "terrainDef" ) )\r
-                       {\r
-                               //% ParseTerrain();\r
-                               Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */\r
-                       }\r
-                       else if( !strcmp( token, "brushDef" ) )\r
-                       {\r
-                               if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )\r
-                                       Error( "Old brush format not allowed in new brush format map" );\r
-                               g_bBrushPrimit = BPRIMIT_NEWBRUSHES;\r
-                               \r
-                               /* parse brush primitive */\r
-                               ParseBrush( onlyLights );\r
-                       }\r
-                       else\r
-                       {\r
-                               if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )\r
-                                       Error( "New brush format not allowed in old brush format map" );\r
-                               g_bBrushPrimit = BPRIMIT_OLDBRUSHES;\r
-                               \r
-                               /* parse old brush format */\r
-                               UnGetToken();\r
-                               ParseBrush( onlyLights );\r
-                       }\r
-                       entitySourceBrushes++;\r
-               }\r
-               else\r
-               {\r
-                       /* parse a key / value pair */\r
-                       ep = ParseEPair();\r
-                       \r
-                       /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */\r
-                       if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )\r
-                       {\r
-                               ep->next = mapEnt->epairs;\r
-                               mapEnt->epairs = ep;\r
-                       }\r
-               }\r
-       }\r
-       \r
-       /* ydnar: get classname */\r
-       classname = ValueForKey( mapEnt, "classname" );\r
-       \r
-       /* ydnar: only lights? */\r
-       if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )\r
-       {\r
-               numEntities--;\r
-               return qtrue;\r
-       }\r
-       \r
-       /* ydnar: determine if this is a func_group */\r
-       if( !Q_stricmp( "func_group", classname ) )\r
-               funcGroup = qtrue;\r
-       else\r
-               funcGroup = qfalse;\r
-       \r
-       /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */\r
-       if( funcGroup || mapEnt->mapEntityNum == 0 )\r
-       {\r
-               //%     Sys_Printf( "World:  %d\n", mapEnt->mapEntityNum );\r
-               castShadows = WORLDSPAWN_CAST_SHADOWS;\r
-               recvShadows = WORLDSPAWN_RECV_SHADOWS;\r
-       }\r
-       \r
-       /* other entities don't cast any shadows, but recv worldspawn shadows */\r
-       else\r
-       {\r
-               //%     Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );\r
-               castShadows = ENTITY_CAST_SHADOWS;\r
-               recvShadows = ENTITY_RECV_SHADOWS;\r
-       }\r
-       \r
-       /* get explicit shadow flags */\r
-       GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );\r
-       \r
-       /* ydnar: get lightmap scaling value for this entity */\r
-       if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||\r
-               strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )\r
-       {\r
-               /* get lightmap scale from entity */\r
-               lightmapScale = FloatForKey( mapEnt, "lightmapscale" );\r
-               if( lightmapScale <= 0.0f )\r
-                       lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );\r
-               if( lightmapScale > 0.0f )\r
-                       Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );\r
-       }\r
-       else\r
-               lightmapScale = 0.0f;\r
-       \r
-       /* ydnar: get cel shader :) for this entity */\r
-       value = ValueForKey( mapEnt, "_celshader" );\r
-       if( value[ 0 ] == '\0' )        \r
-               value = ValueForKey( &entities[ 0 ], "_celshader" );\r
-       if( value[ 0 ] != '\0' )\r
-       {\r
-               sprintf( shader, "textures/%s", value );\r
-               celShader = ShaderInfoForShader( shader );\r
-               Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );\r
-       }\r
-       else\r
-               celShader = NULL;\r
-       \r
-       /* attach stuff to everything in the entity */\r
-       for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )\r
-       {\r
-               brush->entityNum = mapEnt->mapEntityNum;\r
-               brush->castShadows = castShadows;\r
-               brush->recvShadows = recvShadows;\r
-               brush->lightmapScale = lightmapScale;\r
-               brush->celShader = celShader;\r
-       }\r
-       \r
-       for( patch = mapEnt->patches; patch != NULL; patch = patch->next )\r
-       {\r
-               patch->entityNum = mapEnt->mapEntityNum;\r
-               patch->castShadows = castShadows;\r
-               patch->recvShadows = recvShadows;\r
-               patch->lightmapScale = lightmapScale;\r
-               patch->celShader = celShader;\r
-       }\r
-       \r
-       /* ydnar: gs mods: set entity bounds */\r
-       SetEntityBounds( mapEnt );\r
-       \r
-       /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */\r
-       LoadEntityIndexMap( mapEnt );\r
-       \r
-       /* get entity origin and adjust brushes */\r
-       GetVectorForKey( mapEnt, "origin", mapEnt->origin );\r
-       if( mapEnt->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )\r
-               AdjustBrushesForOrigin( mapEnt );\r
-\r
-       /* group_info entities are just for editor grouping (fixme: leak!) */\r
-       if( !Q_stricmp( "group_info", classname ) )\r
-       {\r
-               numEntities--;\r
-               return qtrue;\r
-       }\r
-       \r
-       /* group entities are just for editor convenience, toss all brushes into worldspawn */\r
-       if( funcGroup )\r
-       {\r
-               MoveBrushesToWorld( mapEnt );\r
-               numEntities--;\r
-               return qtrue;\r
-       }\r
-       \r
-       /* done */\r
-       return qtrue;\r
-}\r
-\r
-\r
-\r
-/*\r
-LoadMapFile()\r
-loads a map file into a list of entities\r
-*/\r
-\r
-void LoadMapFile( char *filename, qboolean onlyLights )\r
-{              \r
-       FILE            *file;\r
-       brush_t         *b;\r
-       int                     oldNumEntities, numMapBrushes;\r
-       \r
-       \r
-       /* note it */\r
-       Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );\r
-       Sys_Printf( "Loading %s\n", filename );\r
-       \r
-       /* hack */\r
-       file = SafeOpenRead( filename );\r
-       fclose( file );\r
-       \r
-       /* load the map file */\r
-       LoadScriptFile( filename, -1 );\r
-       \r
-       /* setup */\r
-       if( onlyLights )\r
-               oldNumEntities = numEntities;\r
-       else\r
-               numEntities = 0;\r
-       \r
-       /* initial setup */\r
-       numMapDrawSurfs = 0;\r
-       c_detail = 0;\r
-       g_bBrushPrimit = BPRIMIT_UNDEFINED;\r
-       \r
-       /* allocate a very large temporary brush for building the brushes as they are loaded */\r
-       buildBrush = AllocBrush( MAX_BUILD_SIDES );\r
-       \r
-       /* parse the map file */\r
-       while( ParseMapEntity( onlyLights ) );\r
-       \r
-       /* light loading */\r
-       if( onlyLights )\r
-       {\r
-               /* emit some statistics */\r
-               Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );\r
-       }\r
-       else\r
-       {\r
-               /* set map bounds */\r
-               ClearBounds( mapMins, mapMaxs );\r
-               for( b = entities[ 0 ].brushes; b; b = b->next )\r
-               {\r
-                       AddPointToBounds( b->mins, mapMins, mapMaxs );\r
-                       AddPointToBounds( b->maxs, mapMins, mapMaxs );\r
-               }\r
-               \r
-               /* get brush counts */\r
-               numMapBrushes = CountBrushList( entities[ 0 ].brushes );\r
-               if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )\r
-                       Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );\r
-               \r
-               /* emit some statistics */\r
-               Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );\r
-               Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );\r
-               Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);\r
-               Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);\r
-               Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);\r
-               Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );\r
-               Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);\r
-               Sys_Printf( "%9d areaportals\n", c_areaportals);\r
-               Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",\r
-                       mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],\r
-                       mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);\r
-               \r
-               /* write bogus map */\r
-               if( fakemap )\r
-                       WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );\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 MAP_C
+
+
+
+/* dependencies */
+#include "q3map2.h"
+
+
+
+/* FIXME: remove these vars */
+
+/* undefine to make plane finding use linear sort (note: really slow) */
+#define USE_HASHING
+#define PLANE_HASHES    8192
+
+int planehash[ PLANE_HASHES ];
+
+int c_boxbevels;
+int c_edgebevels;
+int c_areaportals;
+int c_detail;
+int c_structural;
+
+
+
+/*
+   PlaneEqual()
+   ydnar: replaced with variable epsilon for djbob
+ */
+
+qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist ){
+       float ne, de;
+
+
+       /* get local copies */
+       ne = normalEpsilon;
+       de = distanceEpsilon;
+
+       /* compare */
+       // We check equality of each component since we're using '<', not '<='
+       // (the epsilons may be zero).  We want to use '<' intead of '<=' to be
+       // consistent with the true meaning of "epsilon", and also because other
+       // parts of the code uses this inequality.
+       if ( ( p->dist == dist || fabs( p->dist - dist ) < de ) &&
+                ( p->normal[0] == normal[0] || fabs( p->normal[0] - normal[0] ) < ne ) &&
+                ( p->normal[1] == normal[1] || fabs( p->normal[1] - normal[1] ) < ne ) &&
+                ( p->normal[2] == normal[2] || fabs( p->normal[2] - normal[2] ) < ne ) ) {
+               return qtrue;
+       }
+
+       /* different */
+       return qfalse;
+}
+
+
+
+/*
+   AddPlaneToHash()
+ */
+
+void AddPlaneToHash( plane_t *p ){
+       int hash;
+
+
+       hash = ( PLANE_HASHES - 1 ) & (int) fabs( p->dist );
+
+       p->hash_chain = planehash[hash];
+       planehash[hash] = p - mapplanes + 1;
+}
+
+/*
+   ================
+   CreateNewFloatPlane
+   ================
+ */
+int CreateNewFloatPlane( vec3_t normal, vec_t dist ){
+       plane_t *p, temp;
+
+       if ( VectorLength( normal ) < 0.5 ) {
+               Sys_Printf( "FloatPlane: bad normal\n" );
+               return -1;
+       }
+
+       // create a new plane
+       AUTOEXPAND_BY_REALLOC( mapplanes, nummapplanes + 1, allocatedmapplanes, 1024 );
+
+       p = &mapplanes[nummapplanes];
+       VectorCopy( normal, p->normal );
+       p->dist = dist;
+       p->type = ( p + 1 )->type = PlaneTypeForNormal( p->normal );
+
+       VectorSubtract( vec3_origin, normal, ( p + 1 )->normal );
+       ( p + 1 )->dist = -dist;
+
+       nummapplanes += 2;
+
+       // allways put axial planes facing positive first
+       if ( p->type < 3 ) {
+               if ( p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0 ) {
+                       // flip order
+                       temp = *p;
+                       *p = *( p + 1 );
+                       *( p + 1 ) = temp;
+
+                       AddPlaneToHash( p );
+                       AddPlaneToHash( p + 1 );
+                       return nummapplanes - 1;
+               }
+       }
+
+       AddPlaneToHash( p );
+       AddPlaneToHash( p + 1 );
+       return nummapplanes - 2;
+}
+
+
+
+/*
+   SnapNormal()
+   Snaps a near-axial normal vector.
+   Returns qtrue if and only if the normal was adjusted.
+ */
+
+qboolean SnapNormal( vec3_t normal ){
+#if Q3MAP2_EXPERIMENTAL_SNAP_NORMAL_FIX
+       int i;
+       qboolean adjusted = qfalse;
+
+       // A change from the original SnapNormal() is that we snap each
+       // component that's close to 0.  So for example if a normal is
+       // (0.707, 0.707, 0.0000001), it will get snapped to lie perfectly in the
+       // XY plane (its Z component will be set to 0 and its length will be
+       // normalized).  The original SnapNormal() didn't snap such vectors - it
+       // only snapped vectors that were near a perfect axis.
+
+       for ( i = 0; i < 3; i++ )
+       {
+               if ( normal[i] != 0.0 && -normalEpsilon < normal[i] && normal[i] < normalEpsilon ) {
+                       normal[i] = 0.0;
+                       adjusted = qtrue;
+               }
+       }
+
+       if ( adjusted ) {
+               VectorNormalize( normal, normal );
+               return qtrue;
+       }
+       return qfalse;
+#else
+       int i;
+
+       // I would suggest that you uncomment the following code and look at the
+       // results:
+
+       /*
+          Sys_Printf("normalEpsilon is %f\n", normalEpsilon);
+          for (i = 0;; i++)
+          {
+           normal[0] = 1.0;
+           normal[1] = 0.0;
+           normal[2] = i * 0.000001;
+           VectorNormalize(normal, normal);
+           if (1.0 - normal[0] >= normalEpsilon) {
+               Sys_Printf("(%f %f %f)\n", normal[0], normal[1], normal[2]);
+               Error("SnapNormal: test completed");
+           }
+          }
+        */
+
+       // When the normalEpsilon is 0.00001, the loop will break out when normal is
+       // (0.999990 0.000000 0.004469).  In other words, this is the vector closest
+       // to axial that will NOT be snapped.  Anything closer will be snaped.  Now,
+       // 0.004469 is close to 1/225.  The length of a circular quarter-arc of radius
+       // 1 is PI/2, or about 1.57.  And 0.004469/1.57 is about 0.0028, or about
+       // 1/350.  Expressed a different way, 1/350 is also about 0.26/90.
+       // This means is that a normal with an angle that is within 1/4 of a degree
+       // from axial will be "snapped".  My belief is that the person who wrote the
+       // code below did not intend it this way.  I think the person intended that
+       // the epsilon be measured against the vector components close to 0, not 1.0.
+       // I think the logic should be: if 2 of the normal components are within
+       // epsilon of 0, then the vector can be snapped to be perfectly axial.
+       // We may consider adjusting the epsilon to a larger value when we make this
+       // code fix.
+
+       for ( i = 0; i < 3; i++ )
+       {
+               if ( fabs( normal[ i ] - 1 ) < normalEpsilon ) {
+                       VectorClear( normal );
+                       normal[ i ] = 1;
+                       return qtrue;
+               }
+               if ( fabs( normal[ i ] - -1 ) < normalEpsilon ) {
+                       VectorClear( normal );
+                       normal[ i ] = -1;
+                       return qtrue;
+               }
+       }
+       return qfalse;
+#endif
+}
+
+
+
+/*
+   SnapPlane()
+   snaps a plane to normal/distance epsilons
+ */
+
+void SnapPlane( vec3_t normal, vec_t *dist, vec3_t center ){
+// SnapPlane disabled by LordHavoc because it often messes up collision
+// brushes made from triangles of embedded models, and it has little effect
+// on anything else (axial planes are usually derived from snapped points)
+/*
+   SnapPlane reenabled by namespace because of multiple reports of
+   q3map2-crashes which were triggered by this patch.
+ */
+       SnapNormal( normal );
+
+       // TODO: Rambetter has some serious comments here as well.  First off,
+       // in the case where a normal is non-axial, there is nothing special
+       // about integer distances.  I would think that snapping a distance might
+       // make sense for axial normals, but I'm not so sure about snapping
+       // non-axial normals.  A shift by 0.01 in a plane, multiplied by a clipping
+       // against another plane that is 5 degrees off, and we introduce 0.1 error
+       // easily.  A 0.1 error in a vertex is where problems start to happen, such
+       // as disappearing triangles.
+
+       // Second, assuming we have snapped the normal above, let's say that the
+       // plane we just snapped was defined for some points that are actually
+       // quite far away from normal * dist.  Well, snapping the normal in this
+       // case means that we've just moved those points by potentially many units!
+       // Therefore, if we are going to snap the normal, we need to know the
+       // points we're snapping for so that the plane snaps with those points in
+       // mind (points remain close to the plane).
+
+       // I would like to know exactly which problems SnapPlane() is trying to
+       // solve so that we can better engineer it (I'm not saying that SnapPlane()
+       // should be removed altogether).  Fix all this snapping code at some point!
+
+       if ( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon ) {
+               *dist = Q_rint( *dist );
+       }
+}
+
+/*
+   SnapPlaneImproved()
+   snaps a plane to normal/distance epsilons, improved code
+ */
+void SnapPlaneImproved( vec3_t normal, vec_t *dist, int numPoints, const vec3_t *points ){
+       int i;
+       vec3_t center;
+       vec_t distNearestInt;
+
+       if ( SnapNormal( normal ) ) {
+               if ( numPoints > 0 ) {
+                       // Adjust the dist so that the provided points don't drift away.
+                       VectorClear( center );
+                       for ( i = 0; i < numPoints; i++ )
+                       {
+                               VectorAdd( center, points[i], center );
+                       }
+                       for ( i = 0; i < 3; i++ ) { center[i] = center[i] / numPoints; }
+                       *dist = DotProduct( normal, center );
+               }
+       }
+
+       if ( VectorIsOnAxis( normal ) ) {
+               // Only snap distance if the normal is an axis.  Otherwise there
+               // is nothing "natural" about snapping the distance to an integer.
+               distNearestInt = Q_rint( *dist );
+               if ( -distanceEpsilon < *dist - distNearestInt && *dist - distNearestInt < distanceEpsilon ) {
+                       *dist = distNearestInt;
+               }
+       }
+}
+
+
+
+/*
+   FindFloatPlane()
+   ydnar: changed to allow a number of test points to be supplied that
+   must be within an epsilon distance of the plane
+ */
+
+int FindFloatPlane( vec3_t innormal, vec_t dist, int numPoints, vec3_t *points ) // NOTE: this has a side effect on the normal. Good or bad?
+
+#ifdef USE_HASHING
+
+{
+       int i, j, hash, h;
+       int pidx;
+       plane_t *p;
+       vec_t d;
+       vec3_t normal;
+
+       VectorCopy( innormal, normal );
+#if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
+       SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
+#else
+       SnapPlane( normal, &dist );
+#endif
+       /* hash the plane */
+       hash = ( PLANE_HASHES - 1 ) & (int) fabs( dist );
+
+       /* search the border bins as well */
+       for ( i = -1; i <= 1; i++ )
+       {
+               h = ( hash + i ) & ( PLANE_HASHES - 1 );
+               for ( pidx = planehash[ h ] - 1; pidx != -1; pidx = mapplanes[pidx].hash_chain - 1 )
+               {
+                       p = &mapplanes[pidx];
+
+                       /* do standard plane compare */
+                       if ( !PlaneEqual( p, normal, dist ) ) {
+                               continue;
+                       }
+
+                       /* ydnar: uncomment the following line for old-style plane finding */
+                       //%     return p - mapplanes;
+
+                       /* ydnar: test supplied points against this plane */
+                       for ( j = 0; j < numPoints; j++ )
+                       {
+                               // NOTE: When dist approaches 2^16, the resolution of 32 bit floating
+                               // point number is greatly decreased.  The distanceEpsilon cannot be
+                               // very small when world coordinates extend to 2^16.  Making the
+                               // dot product here in 64 bit land will not really help the situation
+                               // because the error will already be carried in dist.
+                               d = DotProduct( points[ j ], p->normal ) - p->dist;
+                               d = fabs( d );
+                               if ( d != 0.0 && d >= distanceEpsilon ) {
+                                       break; // Point is too far from plane.
+                               }
+                       }
+
+                       /* found a matching plane */
+                       if ( j >= numPoints ) {
+                               return p - mapplanes;
+                       }
+               }
+       }
+
+       /* none found, so create a new one */
+       return CreateNewFloatPlane( normal, dist );
+}
+
+#else
+
+{
+       int i;
+       plane_t *p;
+       vec3_t normal;
+
+       VectorCopy( innormal, normal );
+#if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
+       SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
+#else
+       SnapPlane( normal, &dist );
+#endif
+       for ( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
+       {
+               if ( !PlaneEqual( p, normal, dist ) ) {
+                       continue;
+               }
+
+               /* ydnar: uncomment the following line for old-style plane finding */
+               //%     return i;
+
+               /* ydnar: test supplied points against this plane */
+               for ( j = 0; j < numPoints; j++ )
+               {
+                       d = DotProduct( points[ j ], p->normal ) - p->dist;
+                       if ( fabs( d ) > distanceEpsilon ) {
+                               break;
+                       }
+               }
+
+               /* found a matching plane */
+               if ( j >= numPoints ) {
+                       return i;
+               }
+               // TODO: Note that the non-USE_HASHING code does not compute epsilons
+               // for the provided points.  It should do that.  I think this code
+               // is unmaintained because nobody sets USE_HASHING to off.
+       }
+
+       return CreateNewFloatPlane( normal, dist );
+}
+
+#endif
+
+
+
+/*
+   MapPlaneFromPoints()
+   takes 3 points and finds the plane they lie in
+ */
+
+int MapPlaneFromPoints( vec3_t *p ){
+#if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
+       vec3_accu_t paccu[3];
+       vec3_accu_t t1, t2, normalAccu;
+       vec3_t normal;
+       vec_t dist;
+
+       VectorCopyRegularToAccu( p[0], paccu[0] );
+       VectorCopyRegularToAccu( p[1], paccu[1] );
+       VectorCopyRegularToAccu( p[2], paccu[2] );
+
+       VectorSubtractAccu( paccu[0], paccu[1], t1 );
+       VectorSubtractAccu( paccu[2], paccu[1], t2 );
+       CrossProductAccu( t1, t2, normalAccu );
+       VectorNormalizeAccu( normalAccu, normalAccu );
+       // TODO: A 32 bit float for the plane distance isn't enough resolution
+       // if the plane is 2^16 units away from the origin (the "epsilon" approaches
+       // 0.01 in that case).
+       dist = (vec_t) DotProductAccu( paccu[0], normalAccu );
+       VectorCopyAccuToRegular( normalAccu, normal );
+
+       return FindFloatPlane( normal, dist, 3, p );
+#else
+       vec3_t t1, t2, normal;
+       vec_t dist;
+
+
+       /* calc plane normal */
+       VectorSubtract( p[ 0 ], p[ 1 ], t1 );
+       VectorSubtract( p[ 2 ], p[ 1 ], t2 );
+       CrossProduct( t1, t2, normal );
+       VectorNormalize( normal, normal );
+
+       /* calc plane distance */
+       dist = DotProduct( p[ 0 ], normal );
+
+       /* store the plane */
+       return FindFloatPlane( normal, dist, 3, p );
+#endif
+}
+
+
+
+/*
+   SetBrushContents()
+   the content flags and compile flags on all sides of a brush should be the same
+ */
+
+void SetBrushContents( brush_t *b ){
+       int contentFlags, compileFlags;
+       side_t      *s;
+       int i;
+       //%     qboolean        mixed;
+
+
+       /* get initial compile flags from first side */
+       s = &b->sides[ 0 ];
+       contentFlags = s->contentFlags;
+       compileFlags = s->compileFlags;
+       b->contentShader = s->shaderInfo;
+       //%     mixed = qfalse;
+
+       /* get the content/compile flags for every side in the brush */
+       for ( i = 1; i < b->numsides; i++, s++ )
+       {
+               s = &b->sides[ i ];
+               if ( s->shaderInfo == NULL ) {
+                       continue;
+               }
+               //%     if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
+               //%             mixed = qtrue;
+
+               contentFlags |= s->contentFlags;
+               compileFlags |= s->compileFlags;
+       }
+
+       /* ydnar: getting rid of this stupid warning */
+       //%     if( mixed )
+       //%             Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
+
+       /* check for detail & structural */
+       if ( ( compileFlags & C_DETAIL ) && ( compileFlags & C_STRUCTURAL ) ) {
+               xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
+               compileFlags &= ~C_DETAIL;
+       }
+
+       /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
+       if ( fulldetail ) {
+               compileFlags &= ~C_DETAIL;
+       }
+
+       /* all translucent brushes that aren't specifically made structural will be detail */
+       if ( ( compileFlags & C_TRANSLUCENT ) && !( compileFlags & C_STRUCTURAL ) ) {
+               compileFlags |= C_DETAIL;
+       }
+
+       /* detail? */
+       if ( compileFlags & C_DETAIL ) {
+               c_detail++;
+               b->detail = qtrue;
+       }
+       else
+       {
+               c_structural++;
+               b->detail = qfalse;
+       }
+
+       /* opaque? */
+       if ( compileFlags & C_TRANSLUCENT ) {
+               b->opaque = qfalse;
+       }
+       else{
+               b->opaque = qtrue;
+       }
+
+       /* areaportal? */
+       if ( compileFlags & C_AREAPORTAL ) {
+               c_areaportals++;
+       }
+
+       /* set brush flags */
+       b->contentFlags = contentFlags;
+       b->compileFlags = compileFlags;
+}
+
+
+
+/*
+   AddBrushBevels()
+   adds any additional planes necessary to allow the brush being
+   built to be expanded against axial bounding boxes
+   ydnar 2003-01-20: added mrelusive fixes
+ */
+
+void AddBrushBevels( void ){
+       int axis, dir;
+       int i, j, k, l, order;
+       side_t sidetemp;
+       side_t      *s, *s2;
+       winding_t   *w, *w2;
+       vec3_t normal;
+       float dist;
+       vec3_t vec, vec2;
+       float d, minBack;
+
+       //
+       // add the axial planes
+       //
+       order = 0;
+       for ( axis = 0; axis < 3; axis++ ) {
+               for ( dir = -1; dir <= 1; dir += 2, order++ ) {
+                       // see if the plane is allready present
+                       for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
+                       {
+                               /* ydnar: testing disabling of mre code */
+                               #if 0
+                               if ( dir > 0 ) {
+                                       if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
+                                               break;
+                                       }
+                               }
+                               else {
+                                       if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
+                                               break;
+                                       }
+                               }
+                               #else
+                               if ( ( dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
+                                        ( dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f ) ) {
+                                       break;
+                               }
+                               #endif
+                       }
+
+                       if ( i == buildBrush->numsides ) {
+                               // add a new side
+                               if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
+                                       xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
+                               }
+                               memset( s, 0, sizeof( *s ) );
+                               buildBrush->numsides++;
+                               VectorClear( normal );
+                               normal[axis] = dir;
+
+                               if ( dir == 1 ) {
+                                       /* ydnar: adding bevel plane snapping for fewer bsp planes */
+                                       if ( bevelSnap > 0 ) {
+                                               dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
+                                       }
+                                       else{
+                                               dist = buildBrush->maxs[ axis ];
+                                       }
+                               }
+                               else
+                               {
+                                       /* ydnar: adding bevel plane snapping for fewer bsp planes */
+                                       if ( bevelSnap > 0 ) {
+                                               dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
+                                       }
+                                       else{
+                                               dist = -buildBrush->mins[ axis ];
+                                       }
+                               }
+
+                               s->planenum = FindFloatPlane( normal, dist, 0, NULL );
+                               s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
+                               s->bevel = qtrue;
+                               c_boxbevels++;
+                       }
+
+                       // if the plane is not in it canonical order, swap it
+                       if ( i != order ) {
+                               sidetemp = buildBrush->sides[order];
+                               buildBrush->sides[order] = buildBrush->sides[i];
+                               buildBrush->sides[i] = sidetemp;
+                       }
+               }
+       }
+
+       //
+       // add the edge bevels
+       //
+       if ( buildBrush->numsides == 6 ) {
+               return;     // pure axial
+       }
+
+       // test the non-axial plane edges
+       for ( i = 6; i < buildBrush->numsides; i++ ) {
+               s = buildBrush->sides + i;
+               w = s->winding;
+               if ( !w ) {
+                       continue;
+               }
+               for ( j = 0; j < w->numpoints; j++ ) {
+                       k = ( j + 1 ) % w->numpoints;
+                       VectorSubtract( w->p[j], w->p[k], vec );
+                       if ( VectorNormalize( vec, vec ) < 0.5f ) {
+                               continue;
+                       }
+                       SnapNormal( vec );
+                       for ( k = 0; k < 3; k++ ) {
+                               if ( vec[k] == -1.0f || vec[k] == 1.0f || ( vec[k] == 0.0f && vec[( k + 1 ) % 3] == 0.0f ) ) {
+                                       break;  // axial
+                               }
+                       }
+                       if ( k != 3 ) {
+                               continue;   // only test non-axial edges
+                       }
+
+                       /* debug code */
+                       //%     Sys_Printf( "-------------\n" );
+
+                       // try the six possible slanted axials from this edge
+                       for ( axis = 0; axis < 3; axis++ ) {
+                               for ( dir = -1; dir <= 1; dir += 2 ) {
+                                       // construct a plane
+                                       VectorClear( vec2 );
+                                       vec2[axis] = dir;
+                                       CrossProduct( vec, vec2, normal );
+                                       if ( VectorNormalize( normal, normal ) < 0.5f ) {
+                                               continue;
+                                       }
+                                       dist = DotProduct( w->p[j], normal );
+
+                                       // if all the points on all the sides are
+                                       // behind this plane, it is a proper edge bevel
+                                       for ( k = 0; k < buildBrush->numsides; k++ ) {
+
+                                               // if this plane has allready been used, skip it
+                                               if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
+                                                       break;
+                                               }
+
+                                               w2 = buildBrush->sides[k].winding;
+                                               if ( !w2 ) {
+                                                       continue;
+                                               }
+                                               minBack = 0.0f;
+                                               for ( l = 0; l < w2->numpoints; l++ ) {
+                                                       d = DotProduct( w2->p[l], normal ) - dist;
+                                                       if ( d > 0.1f ) {
+                                                               break;  // point in front
+                                                       }
+                                                       if ( d < minBack ) {
+                                                               minBack = d;
+                                                       }
+                                               }
+                                               // if some point was at the front
+                                               if ( l != w2->numpoints ) {
+                                                       break;
+                                               }
+
+                                               // if no points at the back then the winding is on the bevel plane
+                                               if ( minBack > -0.1f ) {
+                                                       //%     Sys_Printf( "On bevel plane\n" );
+                                                       break;
+                                               }
+                                       }
+
+                                       if ( k != buildBrush->numsides ) {
+                                               continue;   // wasn't part of the outer hull
+                                       }
+
+                                       /* debug code */
+                                       //%     Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
+
+                                       // add this plane
+                                       if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
+                                               xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
+                                       }
+                                       s2 = &buildBrush->sides[buildBrush->numsides];
+                                       buildBrush->numsides++;
+                                       memset( s2, 0, sizeof( *s2 ) );
+
+                                       s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
+                                       s2->contentFlags = buildBrush->sides[0].contentFlags;
+                                       s2->bevel = qtrue;
+                                       c_edgebevels++;
+                               }
+                       }
+               }
+       }
+}
+
+
+
+/*
+   FinishBrush()
+   produces a final brush based on the buildBrush->sides array
+   and links it to the current entity
+ */
+
+static void MergeOrigin( entity_t *ent, vec3_t origin ){
+       vec3_t adjustment;
+       char string[128];
+
+       /* we have not parsed the brush completely yet... */
+       GetVectorForKey( ent, "origin", ent->origin );
+
+       VectorMA( origin, -1, ent->originbrush_origin, adjustment );
+       VectorAdd( adjustment, ent->origin, ent->origin );
+       VectorCopy( origin, ent->originbrush_origin );
+
+       sprintf( string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2] );
+       SetKeyValue( ent, "origin", string );
+}
+
+brush_t *FinishBrush( qboolean noCollapseGroups ){
+       brush_t     *b;
+
+
+       /* create windings for sides and bounds for brush */
+       if ( !CreateBrushWindings( buildBrush ) ) {
+               return NULL;
+       }
+
+       /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
+          after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
+       if ( buildBrush->compileFlags & C_ORIGIN ) {
+               vec3_t origin;
+
+               Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
+                                       mapEnt->mapEntityNum, entitySourceBrushes );
+
+               if ( numEntities == 1 ) {
+                       Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
+                                               mapEnt->mapEntityNum, entitySourceBrushes );
+                       return NULL;
+               }
+
+               VectorAdd( buildBrush->mins, buildBrush->maxs, origin );
+               VectorScale( origin, 0.5, origin );
+
+               MergeOrigin( &entities[ numEntities - 1 ], origin );
+
+               /* don't keep this brush */
+               return NULL;
+       }
+
+       /* determine if the brush is an area portal */
+       if ( buildBrush->compileFlags & C_AREAPORTAL ) {
+               if ( numEntities != 1 ) {
+                       Sys_Printf( "Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
+                       return NULL;
+               }
+       }
+
+       /* add bevel planes */
+       if ( !noCollapseGroups ) {
+               AddBrushBevels();
+       }
+
+       /* keep it */
+       b = CopyBrush( buildBrush );
+
+       /* set map entity and brush numbering */
+       b->entityNum = mapEnt->mapEntityNum;
+       b->brushNum = entitySourceBrushes;
+
+       /* set original */
+       b->original = b;
+
+       /* link opaque brushes to head of list, translucent brushes to end */
+       if ( b->opaque || mapEnt->lastBrush == NULL ) {
+               b->next = mapEnt->brushes;
+               mapEnt->brushes = b;
+               if ( mapEnt->lastBrush == NULL ) {
+                       mapEnt->lastBrush = b;
+               }
+       }
+       else
+       {
+               b->next = NULL;
+               mapEnt->lastBrush->next = b;
+               mapEnt->lastBrush = b;
+       }
+
+       /* link colorMod volume brushes to the entity directly */
+       if ( b->contentShader != NULL &&
+                b->contentShader->colorMod != NULL &&
+                b->contentShader->colorMod->type == CM_VOLUME ) {
+               b->nextColorModBrush = mapEnt->colorModBrushes;
+               mapEnt->colorModBrushes = b;
+       }
+
+       /* return to sender */
+       return b;
+}
+
+
+
+/*
+   TextureAxisFromPlane()
+   determines best orthagonal axis to project a texture onto a wall
+   (must be identical in radiant!)
+ */
+
+vec3_t baseaxis[18] =
+{
+       {0,0,1}, {1,0,0}, {0,-1,0},         // floor
+       {0,0,-1}, {1,0,0}, {0,-1,0},        // ceiling
+       {1,0,0}, {0,1,0}, {0,0,-1},         // west wall
+       {-1,0,0}, {0,1,0}, {0,0,-1},        // east wall
+       {0,1,0}, {1,0,0}, {0,0,-1},         // south wall
+       {0,-1,0}, {1,0,0}, {0,0,-1}         // north wall
+};
+
+void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv ){
+       int bestaxis;
+       vec_t dot,best;
+       int i;
+
+       best = 0;
+       bestaxis = 0;
+
+       for ( i = 0 ; i < 6 ; i++ )
+       {
+               dot = DotProduct( pln->normal, baseaxis[i * 3] );
+               if ( dot > best + 0.0001f ) { /* ydnar: bug 637 fix, suggested by jmonroe */
+                       best = dot;
+                       bestaxis = i;
+               }
+       }
+
+       VectorCopy( baseaxis[bestaxis * 3 + 1], xv );
+       VectorCopy( baseaxis[bestaxis * 3 + 2], yv );
+}
+
+
+
+/*
+   QuakeTextureVecs()
+   creates world-to-texture mapping vecs for crappy quake plane arrangements
+ */
+
+void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] ){
+       vec3_t vecs[2];
+       int sv, tv;
+       vec_t ang, sinv, cosv;
+       vec_t ns, nt;
+       int i, j;
+
+
+       TextureAxisFromPlane( plane, vecs[0], vecs[1] );
+
+       if ( !scale[0] ) {
+               scale[0] = 1;
+       }
+       if ( !scale[1] ) {
+               scale[1] = 1;
+       }
+
+       // rotate axis
+       if ( rotate == 0 ) {
+               sinv = 0 ; cosv = 1;
+       }
+       else if ( rotate == 90 ) {
+               sinv = 1 ; cosv = 0;
+       }
+       else if ( rotate == 180 ) {
+               sinv = 0 ; cosv = -1;
+       }
+       else if ( rotate == 270 ) {
+               sinv = -1 ; cosv = 0;
+       }
+       else
+       {
+               ang = rotate / 180 * Q_PI;
+               sinv = sin( ang );
+               cosv = cos( ang );
+       }
+
+       if ( vecs[0][0] ) {
+               sv = 0;
+       }
+       else if ( vecs[0][1] ) {
+               sv = 1;
+       }
+       else{
+               sv = 2;
+       }
+
+       if ( vecs[1][0] ) {
+               tv = 0;
+       }
+       else if ( vecs[1][1] ) {
+               tv = 1;
+       }
+       else{
+               tv = 2;
+       }
+
+       for ( i = 0 ; i < 2 ; i++ ) {
+               ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+               nt = sinv * vecs[i][sv] +  cosv * vecs[i][tv];
+               vecs[i][sv] = ns;
+               vecs[i][tv] = nt;
+       }
+
+       for ( i = 0 ; i < 2 ; i++ )
+               for ( j = 0 ; j < 3 ; j++ )
+                       mappingVecs[i][j] = vecs[i][j] / scale[i];
+
+       mappingVecs[0][3] = shift[0];
+       mappingVecs[1][3] = shift[1];
+}
+
+
+
+/*
+   ParseRawBrush()
+   parses the sides into buildBrush->sides[], nothing else.
+   no validation, back plane removal, etc.
+
+   Timo - 08/26/99
+   added brush epairs parsing ( ignoring actually )
+   Timo - 08/04/99
+   added exclusive brush primitive parsing
+   Timo - 08/08/99
+   support for old brush format back in
+   NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
+ */
+
+static void ParseRawBrush( qboolean onlyLights ){
+       side_t          *side;
+       vec3_t planePoints[ 3 ];
+       int planenum;
+       shaderInfo_t    *si;
+       vec_t shift[ 2 ];
+       vec_t rotate = 0;
+       vec_t scale[ 2 ];
+       char name[ MAX_QPATH ];
+       char shader[ MAX_QPATH ];
+       int flags;
+
+
+       /* initial setup */
+       buildBrush->numsides = 0;
+       buildBrush->detail = qfalse;
+
+       /* bp */
+       if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
+               MatchToken( "{" );
+       }
+
+       /* parse sides */
+       while ( 1 )
+       {
+               if ( !GetToken( qtrue ) ) {
+                       break;
+               }
+               if ( !strcmp( token, "}" ) ) {
+                       break;
+               }
+
+               /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
+               if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
+                       while ( 1 )
+                       {
+                               if ( strcmp( token, "(" ) ) {
+                                       GetToken( qfalse );
+                               }
+                               else{
+                                       break;
+                               }
+                               GetToken( qtrue );
+                       }
+               }
+               UnGetToken();
+
+               /* test side count */
+               if ( buildBrush->numsides >= MAX_BUILD_SIDES ) {
+                       xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
+               }
+
+               /* add side */
+               side = &buildBrush->sides[ buildBrush->numsides ];
+               memset( side, 0, sizeof( *side ) );
+               buildBrush->numsides++;
+
+               /* read the three point plane definition */
+               Parse1DMatrix( 3, planePoints[ 0 ] );
+               Parse1DMatrix( 3, planePoints[ 1 ] );
+               Parse1DMatrix( 3, planePoints[ 2 ] );
+
+               /* bp: read the texture matrix */
+               if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
+                       Parse2DMatrix( 2, 3, (float*) side->texMat );
+               }
+
+               /* read shader name */
+               GetToken( qfalse );
+               strcpy( name, token );
+
+               /* bp */
+               if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
+                       GetToken( qfalse );
+                       shift[ 0 ] = atof( token );
+                       GetToken( qfalse );
+                       shift[ 1 ] = atof( token );
+                       GetToken( qfalse );
+                       rotate = atof( token );
+                       GetToken( qfalse );
+                       scale[ 0 ] = atof( token );
+                       GetToken( qfalse );
+                       scale[ 1 ] = atof( token );
+               }
+
+               /* set default flags and values */
+               sprintf( shader, "textures/%s", name );
+               if ( onlyLights ) {
+                       si = &shaderInfo[ 0 ];
+               }
+               else{
+                       si = ShaderInfoForShader( shader );
+               }
+               side->shaderInfo = si;
+               side->surfaceFlags = si->surfaceFlags;
+               side->contentFlags = si->contentFlags;
+               side->compileFlags = si->compileFlags;
+               side->value = si->value;
+
+               /* ydnar: gs mods: bias texture shift */
+               if ( si->globalTexture == qfalse ) {
+                       shift[ 0 ] -= ( floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth );
+                       shift[ 1 ] -= ( floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight );
+               }
+
+               /*
+                   historically, there are 3 integer values at the end of a brushside line in a .map file.
+                   in quake 3, the only thing that mattered was the first of these three values, which
+                   was previously the content flags. and only then did a single bit matter, the detail
+                   bit. because every game has its own special flags for specifying detail, the
+                   traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
+                   by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
+                   is stored in compileFlags, as opposed to contentFlags, for multiple-game
+                   portability. :sigh:
+                */
+
+               if ( TokenAvailable() ) {
+                       /* get detail bit from map content flags */
+                       GetToken( qfalse );
+                       flags = atoi( token );
+                       if ( flags & C_DETAIL ) {
+                               side->compileFlags |= C_DETAIL;
+                       }
+
+                       /* historical */
+                       GetToken( qfalse );
+                       //% td.flags = atoi( token );
+                       GetToken( qfalse );
+                       //% td.value = atoi( token );
+               }
+
+               /* find the plane number */
+               planenum = MapPlaneFromPoints( planePoints );
+               side->planenum = planenum;
+
+               /* bp: get the texture mapping for this texturedef / plane combination */
+               if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
+                       QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
+               }
+       }
+
+       /* bp */
+       if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
+               UnGetToken();
+               MatchToken( "}" );
+               MatchToken( "}" );
+       }
+}
+
+
+
+/*
+   RemoveDuplicateBrushPlanes
+   returns false if the brush has a mirrored set of planes,
+   meaning it encloses no volume.
+   also removes planes without any normal
+ */
+
+qboolean RemoveDuplicateBrushPlanes( brush_t *b ){
+       int i, j, k;
+       side_t      *sides;
+
+       sides = b->sides;
+
+       for ( i = 1 ; i < b->numsides ; i++ ) {
+
+               // check for a degenerate plane
+               if ( sides[i].planenum == -1 ) {
+                       xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
+                       // remove it
+                       for ( k = i + 1 ; k < b->numsides ; k++ ) {
+                               sides[k - 1] = sides[k];
+                       }
+                       b->numsides--;
+                       i--;
+                       continue;
+               }
+
+               // check for duplication and mirroring
+               for ( j = 0 ; j < i ; j++ ) {
+                       if ( sides[i].planenum == sides[j].planenum ) {
+                               xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
+                               // remove the second duplicate
+                               for ( k = i + 1 ; k < b->numsides ; k++ ) {
+                                       sides[k - 1] = sides[k];
+                               }
+                               b->numsides--;
+                               i--;
+                               break;
+                       }
+
+                       if ( sides[i].planenum == ( sides[j].planenum ^ 1 ) ) {
+                               // mirror plane, brush is invalid
+                               xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
+                               return qfalse;
+                       }
+               }
+       }
+       return qtrue;
+}
+
+
+
+/*
+   ParseBrush()
+   parses a brush out of a map file and sets it up
+ */
+
+static void ParseBrush( qboolean onlyLights, qboolean noCollapseGroups ){
+       /* parse the brush out of the map */
+       ParseRawBrush( onlyLights );
+
+       /* only go this far? */
+       if ( onlyLights ) {
+               return;
+       }
+
+       /* set some defaults */
+       buildBrush->portalareas[ 0 ] = -1;
+       buildBrush->portalareas[ 1 ] = -1;
+       buildBrush->entityNum = numMapEntities - 1;
+       buildBrush->brushNum = entitySourceBrushes;
+
+       /* if there are mirrored planes, the entire brush is invalid */
+       if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
+               return;
+       }
+
+       /* get the content for the entire brush */
+       SetBrushContents( buildBrush );
+
+       /* allow detail brushes to be removed */
+       if ( nodetail && ( buildBrush->compileFlags & C_DETAIL ) ) {
+               //%     FreeBrush( buildBrush );
+               return;
+       }
+
+       /* allow liquid brushes to be removed */
+       if ( nowater && ( buildBrush->compileFlags & C_LIQUID ) ) {
+               //%     FreeBrush( buildBrush );
+               return;
+       }
+
+       /* ydnar: allow hint brushes to be removed */
+       if ( noHint && ( buildBrush->compileFlags & C_HINT ) ) {
+               //%     FreeBrush( buildBrush );
+               return;
+       }
+
+       /* finish the brush */
+       FinishBrush( noCollapseGroups );
+}
+
+
+
+/*
+   MoveBrushesToWorld()
+   takes all of the brushes from the current entity and
+   adds them to the world's brush list
+   (used by func_group)
+ */
+
+void AdjustBrushesForOrigin( entity_t *ent );
+void MoveBrushesToWorld( entity_t *ent ){
+       brush_t     *b, *next;
+       parseMesh_t *pm;
+
+       /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
+       VectorScale( ent->origin, -1, ent->originbrush_origin );
+       AdjustBrushesForOrigin( ent );
+       VectorClear( ent->originbrush_origin );
+
+       /* move brushes */
+       for ( b = ent->brushes; b != NULL; b = next )
+       {
+               /* get next brush */
+               next = b->next;
+
+               /* link opaque brushes to head of list, translucent brushes to end */
+               if ( b->opaque || entities[ 0 ].lastBrush == NULL ) {
+                       b->next = entities[ 0 ].brushes;
+                       entities[ 0 ].brushes = b;
+                       if ( entities[ 0 ].lastBrush == NULL ) {
+                               entities[ 0 ].lastBrush = b;
+                       }
+               }
+               else
+               {
+                       b->next = NULL;
+                       entities[ 0 ].lastBrush->next = b;
+                       entities[ 0 ].lastBrush = b;
+               }
+       }
+       ent->brushes = NULL;
+
+       /* ydnar: move colormod brushes */
+       if ( ent->colorModBrushes != NULL ) {
+               for ( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush ) ;
+
+               b->nextColorModBrush = entities[ 0 ].colorModBrushes;
+               entities[ 0 ].colorModBrushes = ent->colorModBrushes;
+
+               ent->colorModBrushes = NULL;
+       }
+
+       /* move patches */
+       if ( ent->patches != NULL ) {
+               for ( pm = ent->patches; pm->next != NULL; pm = pm->next ) ;
+
+               pm->next = entities[ 0 ].patches;
+               entities[ 0 ].patches = ent->patches;
+
+               ent->patches = NULL;
+       }
+}
+
+
+
+/*
+   AdjustBrushesForOrigin()
+ */
+
+void AdjustBrushesForOrigin( entity_t *ent ){
+
+       int i;
+       side_t      *s;
+       vec_t newdist;
+       brush_t     *b;
+       parseMesh_t *p;
+
+       /* walk brush list */
+       for ( b = ent->brushes; b != NULL; b = b->next )
+       {
+               /* offset brush planes */
+               for ( i = 0; i < b->numsides; i++ )
+               {
+                       /* get brush side */
+                       s = &b->sides[ i ];
+
+                       /* offset side plane */
+                       newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
+
+                       /* find a new plane */
+                       s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
+               }
+
+               /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
+               CreateBrushWindings( b );
+       }
+
+       /* walk patch list */
+       for ( p = ent->patches; p != NULL; p = p->next )
+       {
+               for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
+                       VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
+       }
+}
+
+
+
+/*
+   SetEntityBounds() - ydnar
+   finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
+ */
+
+void SetEntityBounds( entity_t *e ){
+       int i;
+       brush_t *b;
+       parseMesh_t *p;
+       vec3_t mins, maxs;
+       const char  *value;
+
+
+
+
+       /* walk the entity's brushes/patches and determine bounds */
+       ClearBounds( mins, maxs );
+       for ( b = e->brushes; b; b = b->next )
+       {
+               AddPointToBounds( b->mins, mins, maxs );
+               AddPointToBounds( b->maxs, mins, maxs );
+       }
+       for ( p = e->patches; p; p = p->next )
+       {
+               for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
+                       AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
+       }
+
+       /* try to find explicit min/max key */
+       value = ValueForKey( e, "min" );
+       if ( value[ 0 ] != '\0' ) {
+               GetVectorForKey( e, "min", mins );
+       }
+       value = ValueForKey( e, "max" );
+       if ( value[ 0 ] != '\0' ) {
+               GetVectorForKey( e, "max", maxs );
+       }
+
+       /* store the bounds */
+       for ( b = e->brushes; b; b = b->next )
+       {
+               VectorCopy( mins, b->eMins );
+               VectorCopy( maxs, b->eMaxs );
+       }
+       for ( p = e->patches; p; p = p->next )
+       {
+               VectorCopy( mins, p->eMins );
+               VectorCopy( maxs, p->eMaxs );
+       }
+}
+
+
+
+/*
+   LoadEntityIndexMap() - ydnar
+   based on LoadAlphaMap() from terrain.c, a little more generic
+ */
+
+void LoadEntityIndexMap( entity_t *e ){
+       int i, size, numLayers, w, h;
+       const char      *value, *indexMapFilename, *shader;
+       char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
+       byte            *pixels;
+       unsigned int    *pixels32;
+       indexMap_t      *im;
+       brush_t         *b;
+       parseMesh_t     *p;
+
+
+       /* this only works with bmodel ents */
+       if ( e->brushes == NULL && e->patches == NULL ) {
+               return;
+       }
+
+       /* determine if there is an index map (support legacy "alphamap" key as well) */
+       value = ValueForKey( e, "_indexmap" );
+       if ( value[ 0 ] == '\0' ) {
+               value = ValueForKey( e, "alphamap" );
+       }
+       if ( value[ 0 ] == '\0' ) {
+               return;
+       }
+       indexMapFilename = value;
+
+       /* get number of layers (support legacy "layers" key as well) */
+       value = ValueForKey( e, "_layers" );
+       if ( value[ 0 ] == '\0' ) {
+               value = ValueForKey( e, "layers" );
+       }
+       if ( value[ 0 ] == '\0' ) {
+               Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
+               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+               return;
+       }
+       numLayers = atoi( value );
+       if ( numLayers < 1 ) {
+               Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
+               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+               return;
+       }
+
+       /* get base shader name (support legacy "shader" key as well) */
+       value = ValueForKey( mapEnt, "_shader" );
+       if ( value[ 0 ] == '\0' ) {
+               value = ValueForKey( e, "shader" );
+       }
+       if ( value[ 0 ] == '\0' ) {
+               Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
+               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+               return;
+       }
+       shader = value;
+
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n",  mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
+
+       /* get index map file extension */
+       ExtractFileExtension( indexMapFilename, ext );
+
+       /* handle tga image */
+       if ( !Q_stricmp( ext, "tga" ) ) {
+               /* load it */
+               Load32BitImage( indexMapFilename, &pixels32, &w, &h );
+
+               /* convert to bytes */
+               size = w * h;
+               pixels = safe_malloc( size );
+               for ( i = 0; i < size; i++ )
+               {
+                       pixels[ i ] = ( ( pixels32[ i ] & 0xFF ) * numLayers ) / 256;
+                       if ( pixels[ i ] >= numLayers ) {
+                               pixels[ i ] = numLayers - 1;
+                       }
+               }
+
+               /* free the 32 bit image */
+               free( pixels32 );
+       }
+       else
+       {
+               /* load it */
+               Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
+
+               /* debug code */
+               //%     Sys_Printf( "-------------------------------" );
+
+               /* fix up out-of-range values */
+               size = w * h;
+               for ( i = 0; i < size; i++ )
+               {
+                       if ( pixels[ i ] >= numLayers ) {
+                               pixels[ i ] = numLayers - 1;
+                       }
+
+                       /* debug code */
+                       //%     if( (i % w) == 0 )
+                       //%             Sys_Printf( "\n" );
+                       //%     Sys_Printf( "%c", pixels[ i ] + '0' );
+               }
+
+               /* debug code */
+               //%     Sys_Printf( "\n-------------------------------\n" );
+       }
+
+       /* the index map must be at least 2x2 pixels */
+       if ( w < 2 || h < 2 ) {
+               Sys_FPrintf( SYS_WRN, "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
+               Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
+               free( pixels );
+               return;
+       }
+
+       /* create a new index map */
+       im = safe_malloc( sizeof( *im ) );
+       memset( im, 0, sizeof( *im ) );
+
+       /* set it up */
+       im->w = w;
+       im->h = h;
+       im->numLayers = numLayers;
+       strcpy( im->name, indexMapFilename );
+       strcpy( im->shader, shader );
+       im->pixels = pixels;
+
+       /* get height offsets */
+       value = ValueForKey( mapEnt, "_offsets" );
+       if ( value[ 0 ] == '\0' ) {
+               value = ValueForKey( e, "offsets" );
+       }
+       if ( value[ 0 ] != '\0' ) {
+               /* value is a space-seperated set of numbers */
+               strcpy( offset, value );
+               search = offset;
+
+               /* get each value */
+               for ( i = 0; i < 256 && *search != '\0'; i++ )
+               {
+                       space = strstr( search, " " );
+                       if ( space != NULL ) {
+                               *space = '\0';
+                       }
+                       im->offsets[ i ] = atof( search );
+                       if ( space == NULL ) {
+                               break;
+                       }
+                       search = space + 1;
+               }
+       }
+
+       /* store the index map in every brush/patch in the entity */
+       for ( b = e->brushes; b != NULL; b = b->next )
+               b->im = im;
+       for ( p = e->patches; p != NULL; p = p->next )
+               p->im = im;
+}
+
+
+
+
+
+
+
+/*
+   ParseMapEntity()
+   parses a single entity out of a map file
+ */
+
+static qboolean ParseMapEntity( qboolean onlyLights, qboolean noCollapseGroups ){
+       epair_t         *ep;
+       const char      *classname, *value;
+       float lightmapScale, shadeAngle;
+       int lightmapSampleSize;
+       char shader[ MAX_QPATH ];
+       shaderInfo_t    *celShader = NULL;
+       brush_t         *brush;
+       parseMesh_t     *patch;
+       qboolean funcGroup;
+       int castShadows, recvShadows;
+
+
+       /* eof check */
+       if ( !GetToken( qtrue ) ) {
+               return qfalse;
+       }
+
+       /* conformance check */
+       if ( strcmp( token, "{" ) ) {
+               Sys_FPrintf( SYS_WRN, "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
+                                       "Continuing to process map, but resulting BSP may be invalid.\n",
+                                       token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
+               return qfalse;
+       }
+
+       /* range check */
+       AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
+
+       /* setup */
+       entitySourceBrushes = 0;
+       mapEnt = &entities[ numEntities ];
+       numEntities++;
+       memset( mapEnt, 0, sizeof( *mapEnt ) );
+
+       /* ydnar: true entity numbering */
+       mapEnt->mapEntityNum = numMapEntities;
+       numMapEntities++;
+
+       /* loop */
+       while ( 1 )
+       {
+               /* get initial token */
+               if ( !GetToken( qtrue ) ) {
+                       Sys_FPrintf( SYS_WRN, "WARNING: ParseEntity: EOF without closing brace\n"
+                                               "Continuing to process map, but resulting BSP may be invalid.\n" );
+                       return qfalse;
+               }
+
+               if ( !strcmp( token, "}" ) ) {
+                       break;
+               }
+
+               if ( !strcmp( token, "{" ) ) {
+                       /* parse a brush or patch */
+                       if ( !GetToken( qtrue ) ) {
+                               break;
+                       }
+
+                       /* check */
+                       if ( !strcmp( token, "patchDef2" ) ) {
+                               numMapPatches++;
+                               ParsePatch( onlyLights );
+                       }
+                       else if ( !strcmp( token, "terrainDef" ) ) {
+                               //% ParseTerrain();
+                               Sys_FPrintf( SYS_WRN, "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
+                       }
+                       else if ( !strcmp( token, "brushDef" ) ) {
+                               if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
+                                       Error( "Old brush format not allowed in new brush format map" );
+                               }
+                               g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
+
+                               /* parse brush primitive */
+                               ParseBrush( onlyLights, noCollapseGroups );
+                       }
+                       else
+                       {
+                               if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
+                                       Error( "New brush format not allowed in old brush format map" );
+                               }
+                               g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
+
+                               /* parse old brush format */
+                               UnGetToken();
+                               ParseBrush( onlyLights, noCollapseGroups );
+                       }
+                       entitySourceBrushes++;
+               }
+               else
+               {
+                       /* parse a key / value pair */
+                       ep = ParseEPair();
+
+                       /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
+                       if ( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' ) {
+                               ep->next = mapEnt->epairs;
+                               mapEnt->epairs = ep;
+                       }
+               }
+       }
+
+       /* ydnar: get classname */
+       classname = ValueForKey( mapEnt, "classname" );
+
+       /* ydnar: only lights? */
+       if ( onlyLights && Q_strncasecmp( classname, "light", 5 ) ) {
+               numEntities--;
+               return qtrue;
+       }
+
+       /* ydnar: determine if this is a func_group */
+       if ( !Q_stricmp( "func_group", classname ) ) {
+               funcGroup = qtrue;
+       }
+       else{
+               funcGroup = qfalse;
+       }
+
+       /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
+       if ( funcGroup || mapEnt->mapEntityNum == 0 ) {
+               //%     Sys_Printf( "World:  %d\n", mapEnt->mapEntityNum );
+               castShadows = WORLDSPAWN_CAST_SHADOWS;
+               recvShadows = WORLDSPAWN_RECV_SHADOWS;
+       }
+
+       /* other entities don't cast any shadows, but recv worldspawn shadows */
+       else
+       {
+               //%     Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
+               castShadows = ENTITY_CAST_SHADOWS;
+               recvShadows = ENTITY_RECV_SHADOWS;
+       }
+
+       /* get explicit shadow flags */
+       GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
+
+       /* vortex: added _ls key (short name of lightmapscale) */
+       /* ydnar: get lightmap scaling value for this entity */
+       lightmapScale = 0.0f;
+       if ( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
+                strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ||
+                strcmp( "", ValueForKey( mapEnt, "_ls" ) ) ) {
+               /* get lightmap scale from entity */
+               lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
+               if ( lightmapScale <= 0.0f ) {
+                       lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
+               }
+               if ( lightmapScale <= 0.0f ) {
+                       lightmapScale = FloatForKey( mapEnt, "_ls" );
+               }
+               if ( lightmapScale < 0.0f ) {
+                       lightmapScale = 0.0f;
+               }
+               if ( lightmapScale > 0.0f ) {
+                       Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
+               }
+       }
+
+       /* ydnar: get cel shader :) for this entity */
+       value = ValueForKey( mapEnt, "_celshader" );
+       if ( value[ 0 ] == '\0' ) {
+               value = ValueForKey( &entities[ 0 ], "_celshader" );
+       }
+       if ( value[ 0 ] != '\0' ) {
+               if ( strcmp( value, "none" ) ) {
+                       sprintf( shader, "textures/%s", value );
+                       celShader = ShaderInfoForShader( shader );
+                       Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
+               }
+               else
+               {
+                       celShader = NULL;
+               }
+       }
+       else{
+               celShader = ( *globalCelShader ? ShaderInfoForShader( globalCelShader ) : NULL );
+       }
+
+       /* jal : entity based _shadeangle */
+       shadeAngle = 0.0f;
+       if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) ) {
+               shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
+       }
+       /* vortex' aliases */
+       else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) ) {
+               shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
+       }
+       else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) ) {
+               shadeAngle = FloatForKey( mapEnt, "_sn" );
+       }
+       else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) ) {
+               shadeAngle = FloatForKey( mapEnt, "_smooth" );
+       }
+
+       if ( shadeAngle < 0.0f ) {
+               shadeAngle = 0.0f;
+       }
+
+       if ( shadeAngle > 0.0f ) {
+               Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
+       }
+
+       /* jal : entity based _samplesize */
+       lightmapSampleSize = 0;
+       if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) ) {
+               lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
+       }
+       else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) ) {
+               lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
+       }
+
+       if ( lightmapSampleSize < 0 ) {
+               lightmapSampleSize = 0;
+       }
+
+       if ( lightmapSampleSize > 0 ) {
+               Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
+       }
+
+       /* attach stuff to everything in the entity */
+       for ( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
+       {
+               brush->entityNum = mapEnt->mapEntityNum;
+               brush->castShadows = castShadows;
+               brush->recvShadows = recvShadows;
+               brush->lightmapSampleSize = lightmapSampleSize;
+               brush->lightmapScale = lightmapScale;
+               brush->celShader = celShader;
+               brush->shadeAngleDegrees = shadeAngle;
+       }
+
+       for ( patch = mapEnt->patches; patch != NULL; patch = patch->next )
+       {
+               patch->entityNum = mapEnt->mapEntityNum;
+               patch->castShadows = castShadows;
+               patch->recvShadows = recvShadows;
+               patch->lightmapSampleSize = lightmapSampleSize;
+               patch->lightmapScale = lightmapScale;
+               patch->celShader = celShader;
+       }
+
+       /* ydnar: gs mods: set entity bounds */
+       SetEntityBounds( mapEnt );
+
+       /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
+       LoadEntityIndexMap( mapEnt );
+
+       /* get entity origin and adjust brushes */
+       GetVectorForKey( mapEnt, "origin", mapEnt->origin );
+       if ( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] ) {
+               AdjustBrushesForOrigin( mapEnt );
+       }
+
+       /* group_info entities are just for editor grouping (fixme: leak!) */
+       if ( !noCollapseGroups && !Q_stricmp( "group_info", classname ) ) {
+               numEntities--;
+               return qtrue;
+       }
+
+       /* group entities are just for editor convenience, toss all brushes into worldspawn */
+       if ( !noCollapseGroups && funcGroup ) {
+               MoveBrushesToWorld( mapEnt );
+               numEntities--;
+               return qtrue;
+       }
+
+       /* done */
+       return qtrue;
+}
+
+
+
+/*
+   LoadMapFile()
+   loads a map file into a list of entities
+ */
+
+void LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups ){
+       FILE        *file;
+       brush_t     *b;
+       int oldNumEntities = 0, numMapBrushes;
+
+
+       /* note it */
+       Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
+       Sys_Printf( "Loading %s\n", filename );
+
+       /* hack */
+       file = SafeOpenRead( filename );
+       fclose( file );
+
+       /* load the map file */
+       LoadScriptFile( filename, -1 );
+
+       /* setup */
+       if ( onlyLights ) {
+               oldNumEntities = numEntities;
+       }
+       else{
+               numEntities = 0;
+       }
+
+       /* initial setup */
+       numMapDrawSurfs = 0;
+       c_detail = 0;
+       g_bBrushPrimit = BPRIMIT_UNDEFINED;
+
+       /* allocate a very large temporary brush for building the brushes as they are loaded */
+       buildBrush = AllocBrush( MAX_BUILD_SIDES );
+
+       /* parse the map file */
+       while ( ParseMapEntity( onlyLights, noCollapseGroups ) ) ;
+
+       /* light loading */
+       if ( onlyLights ) {
+               /* emit some statistics */
+               Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
+       }
+       else
+       {
+               /* set map bounds */
+               ClearBounds( mapMins, mapMaxs );
+               for ( b = entities[ 0 ].brushes; b; b = b->next )
+               {
+                       AddPointToBounds( b->mins, mapMins, mapMaxs );
+                       AddPointToBounds( b->maxs, mapMins, mapMaxs );
+               }
+
+               /* get brush counts */
+               numMapBrushes = CountBrushList( entities[ 0 ].brushes );
+               if ( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 ) {
+                       Sys_FPrintf( SYS_WRN, "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
+               }
+
+               /* emit some statistics */
+               Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
+               Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
+               Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches );
+               Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels );
+               Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels );
+               Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
+               Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes );
+               Sys_Printf( "%9d areaportals\n", c_areaportals );
+               Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
+                                       mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
+                                       mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ] );
+
+               /* write bogus map */
+               if ( fakemap ) {
+                       WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );
+               }
+       }
+}