]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/map.c
eol style
[xonotic/netradiant.git] / tools / quake3 / q3map2 / map.c
index dbc71f1438fbbe391936f6c647dd7188c8d01d1e..633d327a24339a80ec167af808e8a3d2cc0adfe2 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
+
+plane_t                                        *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
+*/
+
+#define        NORMAL_EPSILON  0.00001
+#define        DIST_EPSILON    0.01
+
+qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )
+{
+       float   ne, de;
+       
+       
+       /* get local copies */
+       ne = normalEpsilon;
+       de = distanceEpsilon;
+       
+       /* compare */
+       if( fabs( p->dist - dist ) <= de &&
+               fabs( p->normal[ 0 ] - normal[ 0 ] ) <= ne &&
+               fabs( p->normal[ 1 ] - normal[ 1 ] ) <= ne &&
+               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;
+}
+
+/*
+================
+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
+       if (nummapplanes+2 > MAX_MAP_PLANES)
+               Error ("MAX_MAP_PLANES");
+
+       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
+*/
+
+void SnapNormal( vec3_t normal )
+{
+       int             i;
+
+       for( i = 0; i < 3; i++ )
+       {
+               if( fabs( normal[ i ] - 1 ) < normalEpsilon )
+               {
+                       VectorClear( normal );
+                       normal[ i ] = 1;
+                       break;
+               }
+               if( fabs( normal[ i ] - -1 ) < normalEpsilon )
+               {
+                       VectorClear( normal );
+                       normal[ i ] = -1;
+                       break;
+               }
+       }
+}
+
+
+
+/*
+SnapPlane()
+snaps a plane to normal/distance epsilons
+*/
+
+void SnapPlane( vec3_t normal, vec_t *dist )
+{
+       SnapNormal( normal );
+
+       if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
+               *dist = Q_rint( *dist );
+}
+
+
+
+/*
+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 normal, vec_t dist, int numPoints, vec3_t *points )
+
+#ifdef USE_HASHING
+
+{
+       int             i, j, hash, h;
+       plane_t *p;
+       vec_t   d;
+       
+       
+       /* hash the plane */
+       SnapPlane( normal, &dist );
+       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( p = planehash[ h ]; p != NULL; p = p->hash_chain )
+               {
+                       /* 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++ )
+                       {
+                               d = DotProduct( points[ j ], normal ) - dist;
+                               if( fabs( d ) > distanceEpsilon )
+                                       break;
+                       }
+                       
+                       /* 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;
+       
+
+       SnapPlane( normal, &dist );
+       for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
+       {
+               if( PlaneEqual( p, normal, dist ) )
+                       return i;
+       }
+       
+       return CreateNewFloatPlane( normal, dist );
+}
+
+#endif
+
+
+
+/*
+MapPlaneFromPoints()
+takes 3 points and finds the plane they lie in
+*/
+
+int MapPlaneFromPoints( vec3_t *p )
+{
+       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 );
+}
+
+
+
+/*
+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;
+       }
+       
+       /* 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
+*/
+
+brush_t *FinishBrush( void )
+{
+       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 )
+       {
+               char    string[ 32 ];
+               vec3_t  origin;
+
+               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);
+
+               sprintf( string, "%i %i %i", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
+               SetKeyValue( &entities[ numEntities - 1 ], "origin", string);
+
+               VectorCopy( origin, 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 */
+       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;
+       }
+       
+       /* 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;
+       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 )
+{
+       brush_t *b;
+       
+       
+       /* 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 */
+       b = FinishBrush();
+}
+
+
+
+/*
+MoveBrushesToWorld()
+takes all of the brushes from the current entity and
+adds them to the world's brush list
+(used by func_group)
+*/
+
+void MoveBrushesToWorld( entity_t *ent )
+{
+       brush_t         *b, *next;
+       parseMesh_t     *pm;
+
+       
+       /* 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;
+               }
+               
+               //%     b->next = entities[ 0 ].brushes;
+               //%     entities[ 0 ].brushes = b;
+       }
+       ent->brushes = NULL;
+       
+       /* move patches */
+       if( ent->patches != NULL )
+       {
+               for( pm = ent->patches; pm->next; 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->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->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_Printf( "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_Printf( "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_Printf( "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_Printf( "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 )
+{
+       epair_t                 *ep;
+       const char              *classname, *value;
+       float                   lightmapScale;
+       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_Printf( "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 */
+       if( numEntities >= MAX_MAP_ENTITIES )
+               Error( "numEntities == MAX_MAP_ENTITIES" );
+       
+       /* 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_Printf( "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_Printf( "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 );
+                       }
+                       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 );
+                       }
+                       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 );
+       
+       /* ydnar: get lightmap scaling value for this entity */
+       if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
+               strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) )
+       {
+               /* get lightmap scale from entity */
+               lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
+               if( lightmapScale <= 0.0f )
+                       lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
+               if( lightmapScale > 0.0f )
+                       Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
+       }
+       else
+               lightmapScale = 0.0f;
+       
+       /* ydnar: get cel shader :) for this entity */
+       value = ValueForKey( mapEnt, "_celshader" );
+       if( value[ 0 ] == '\0' )        
+               value = ValueForKey( &entities[ 0 ], "_celshader" );
+       if( value[ 0 ] != '\0' )
+       {
+               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;
+       
+       /* 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->lightmapScale = lightmapScale;
+               brush->celShader = celShader;
+       }
+       
+       for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
+       {
+               patch->entityNum = mapEnt->mapEntityNum;
+               patch->castShadows = castShadows;
+               patch->recvShadows = recvShadows;
+               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->origin[ 0 ] || mapEnt->origin[ 1 ] || mapEnt->origin[ 2 ] )
+               AdjustBrushesForOrigin( mapEnt );
+
+       /* group_info entities are just for editor grouping (fixme: leak!) */
+       if( !Q_stricmp( "group_info", classname ) )
+       {
+               numEntities--;
+               return qtrue;
+       }
+       
+       /* group entities are just for editor convenience, toss all brushes into worldspawn */
+       if( 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 )
+{              
+       FILE            *file;
+       brush_t         *b;
+       int                     oldNumEntities, 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 ) );
+       
+       /* 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_Printf( "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 );
+       }
+}