]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/q2map/map.c
Merge commit '6592d65469e5386216a692ba3b5d6e7cc590c617' into garux-merge
[xonotic/netradiant.git] / tools / quake2 / q2map / map.c
index ac3be9d89d128442bc5bdd1c753ba2554610a032..f812393e784c71e4ef5dda9ac5d7f440e6e9ea5e 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
-// map.c\r
-\r
-#include "qbsp.h"\r
-\r
-extern qboolean onlyents;\r
-\r
-int                    nummapbrushes;\r
-mapbrush_t     mapbrushes[MAX_MAP_BRUSHES];\r
-\r
-int                    nummapbrushsides;\r
-side_t         brushsides[MAX_MAP_SIDES];\r
-brush_texture_t        side_brushtextures[MAX_MAP_SIDES];\r
-\r
-int                    nummapplanes;\r
-plane_t                mapplanes[MAX_MAP_PLANES];\r
-\r
-#define        PLANE_HASHES    1024\r
-plane_t                *planehash[PLANE_HASHES];\r
-\r
-vec3_t         map_mins, map_maxs;\r
-\r
-// undefine to make plane finding use linear sort\r
-#define        USE_HASHING\r
-\r
-void TestExpandBrushes (void);\r
-\r
-int            c_boxbevels;\r
-int            c_edgebevels;\r
-\r
-int            c_areaportals;\r
-\r
-int            c_clipbrushes;\r
-\r
-/*\r
-=============================================================================\r
-\r
-PLANE FINDING\r
-\r
-=============================================================================\r
-*/\r
-\r
-\r
-/*\r
-=================\r
-PlaneTypeForNormal\r
-=================\r
-*/\r
-int    PlaneTypeForNormal (vec3_t normal)\r
-{\r
-       vec_t   ax, ay, az;\r
-       \r
-// NOTE: should these have an epsilon around 1.0?              \r
-       if (normal[0] == 1.0 || normal[0] == -1.0)\r
-               return PLANE_X;\r
-       if (normal[1] == 1.0 || normal[1] == -1.0)\r
-               return PLANE_Y;\r
-       if (normal[2] == 1.0 || normal[2] == -1.0)\r
-               return PLANE_Z;\r
-               \r
-       ax = fabs(normal[0]);\r
-       ay = fabs(normal[1]);\r
-       az = fabs(normal[2]);\r
-       \r
-       if (ax >= ay && ax >= az)\r
-               return PLANE_ANYX;\r
-       if (ay >= ax && ay >= az)\r
-               return PLANE_ANYY;\r
-       return PLANE_ANYZ;\r
-}\r
-\r
-/*\r
-================\r
-PlaneEqual\r
-================\r
-*/\r
-#define        NORMAL_EPSILON  0.00001\r
-#define        DIST_EPSILON    0.01\r
-qboolean       PlaneEqual (plane_t *p, vec3_t normal, vec_t dist)\r
-{\r
-#if 1\r
-       if (\r
-          fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON\r
-       && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON\r
-       && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON\r
-       && fabs(p->dist - dist) < DIST_EPSILON )\r
-               return true;\r
-#else\r
-       if (p->normal[0] == normal[0]\r
-               && p->normal[1] == normal[1]\r
-               && p->normal[2] == normal[2]\r
-               && p->dist == dist)\r
-               return true;\r
-#endif\r
-       return false;\r
-}\r
-\r
-/*\r
-================\r
-AddPlaneToHash\r
-================\r
-*/\r
-void   AddPlaneToHash (plane_t *p)\r
-{\r
-       int             hash;\r
-\r
-       hash = (int)fabs(p->dist) / 8;\r
-       hash &= (PLANE_HASHES-1);\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
-               Error ("FloatPlane: bad normal");\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
-SnapVector\r
-==============\r
-*/\r
-void   SnapVector (vec3_t normal)\r
-{\r
-       int             i;\r
-\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               if ( fabs(normal[i] - 1) < NORMAL_EPSILON )\r
-               {\r
-                       VectorClear (normal);\r
-                       normal[i] = 1;\r
-                       break;\r
-               }\r
-               if ( fabs(normal[i] - -1) < NORMAL_EPSILON )\r
-               {\r
-                       VectorClear (normal);\r
-                       normal[i] = -1;\r
-                       break;\r
-               }\r
-       }\r
-}\r
-\r
-/*\r
-==============\r
-SnapPlane\r
-==============\r
-*/\r
-void   SnapPlane (vec3_t normal, vec_t *dist)\r
-{\r
-       SnapVector (normal);\r
-\r
-       if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)\r
-               *dist = Q_rint(*dist);\r
-}\r
-\r
-/*\r
-=============\r
-FindFloatPlane\r
-\r
-=============\r
-*/\r
-#ifndef USE_HASHING\r
-int            FindFloatPlane (vec3_t normal, vec_t dist)\r
-{\r
-       int             i;\r
-       plane_t *p;\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
-#else\r
-int            FindFloatPlane (vec3_t normal, vec_t dist)\r
-{\r
-       int             i;\r
-       plane_t *p;\r
-       int             hash, h;\r
-\r
-       SnapPlane (normal, &dist);\r
-       hash = (int)fabs(dist) / 8;\r
-       hash &= (PLANE_HASHES-1);\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 ; p=p->hash_chain)\r
-               {\r
-                       if (PlaneEqual (p, normal, dist))\r
-                               return p-mapplanes;\r
-               }\r
-       }\r
-\r
-       return CreateNewFloatPlane (normal, dist);\r
-}\r
-#endif\r
-\r
-/*\r
-================\r
-PlaneFromPoints\r
-================\r
-*/\r
-int PlaneFromPoints (int *p0, int *p1, int *p2)\r
-{\r
-       vec3_t  t1, t2, normal;\r
-       vec_t   dist;\r
-\r
-       VectorSubtract (p0, p1, t1);\r
-       VectorSubtract (p2, p1, t2);\r
-       CrossProduct (t1, t2, normal);\r
-       VectorNormalize (normal, normal);\r
-\r
-       dist = DotProduct (p0, normal);\r
-\r
-       return FindFloatPlane (normal, dist);\r
-}\r
-\r
-\r
-//====================================================================\r
-\r
-\r
-/*\r
-===========\r
-BrushContents\r
-===========\r
-*/\r
-int    BrushContents (mapbrush_t *b)\r
-{\r
-       int                     contents;\r
-       side_t          *s;\r
-       int                     i;\r
-       int                     trans;\r
-\r
-       s = &b->original_sides[0];\r
-       contents = s->contents;\r
-       trans = texinfo[s->texinfo].flags;\r
-       for (i=1 ; i<b->numsides ; i++, s++)\r
-       {\r
-               s = &b->original_sides[i];\r
-               trans |= texinfo[s->texinfo].flags;\r
-               if (s->contents != contents)\r
-               {\r
-                       Sys_Printf ("Entity %i, Brush %i: mixed face contents\n"\r
-                               , b->entitynum, b->brushnum);\r
-                       break;\r
-               }\r
-       }\r
-\r
-       // if any side is translucent, mark the contents\r
-       // and change solid to window\r
-       if ( trans & (SURF_TRANS33|SURF_TRANS66) )\r
-       {\r
-               contents |= CONTENTS_TRANSLUCENT;\r
-               if (contents & CONTENTS_SOLID)\r
-               {\r
-                       contents &= ~CONTENTS_SOLID;\r
-                       contents |= CONTENTS_WINDOW;\r
-               }\r
-       }\r
-\r
-       return contents;\r
-}\r
-\r
-\r
-//============================================================================\r
-\r
-/*\r
-=================\r
-AddBrushBevels\r
-\r
-Adds any additional planes necessary to allow the brush to be expanded\r
-against axial bounding boxes\r
-=================\r
-*/\r
-void AddBrushBevels (mapbrush_t *b)\r
-{\r
-       int             axis, dir;\r
-       int             i, j, k, l, order;\r
-       side_t  sidetemp;\r
-       brush_texture_t tdtemp;\r
-       side_t  *s, *s2;\r
-       vec3_t  normal;\r
-       float   dist;\r
-       winding_t       *w, *w2;\r
-       vec3_t  vec, vec2;\r
-       float   d;\r
-\r
-       //\r
-       // add the axial planes\r
-       //\r
-       order = 0;\r
-       for (axis=0 ; axis <3 ; axis++)\r
-       {\r
-               for (dir=-1 ; dir <= 1 ; dir+=2, order++)\r
-               {\r
-                       // see if the plane is allready present\r
-                       for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)\r
-                       {\r
-                               if (mapplanes[s->planenum].normal[axis] == dir)\r
-                                       break;\r
-                       }\r
-\r
-                       if (i == b->numsides)\r
-                       {       // add a new side\r
-                               if (nummapbrushsides == MAX_MAP_BRUSHSIDES)\r
-                                       Error ("MAX_MAP_BRUSHSIDES");\r
-                               nummapbrushsides++;\r
-                               b->numsides++;\r
-                               VectorClear (normal);\r
-                               normal[axis] = dir;\r
-                               if (dir == 1)\r
-                                       dist = b->maxs[axis];\r
-                               else\r
-                                       dist = -b->mins[axis];\r
-                               s->planenum = FindFloatPlane (normal, dist);\r
-                               s->texinfo = b->original_sides[0].texinfo;\r
-                               s->contents = b->original_sides[0].contents;\r
-                               s->bevel = true;\r
-                               c_boxbevels++;\r
-                       }\r
-\r
-                       // if the plane is not in it canonical order, swap it\r
-                       if (i != order)\r
-                       {\r
-                               sidetemp = b->original_sides[order];\r
-                               b->original_sides[order] = b->original_sides[i];\r
-                               b->original_sides[i] = sidetemp;\r
-\r
-                               j = b->original_sides - brushsides;\r
-                               tdtemp = side_brushtextures[j+order];\r
-                               side_brushtextures[j+order] = side_brushtextures[j+i];\r
-                               side_brushtextures[j+i] = tdtemp;\r
-                       }\r
-               }\r
-       }\r
-\r
-       //\r
-       // add the edge bevels\r
-       //\r
-       if (b->numsides == 6)\r
-               return;         // pure axial\r
-\r
-       // test the non-axial plane edges\r
-       for (i=6 ; i<b->numsides ; i++)\r
-       {\r
-               s = b->original_sides + i;\r
-               w = s->winding;\r
-               if (!w)\r
-                       continue;\r
-               for (j=0 ; j<w->numpoints ; j++)\r
-               {\r
-                       k = (j+1)%w->numpoints;\r
-                       VectorSubtract (w->p[j], w->p[k], vec);\r
-                       if (VectorNormalize (vec, vec) < 0.5)\r
-                               continue;\r
-                       SnapVector (vec);\r
-                       for (k=0 ; k<3 ; k++)\r
-                               if ( vec[k] == -1 || vec[k] == 1)\r
-                                       break;  // axial\r
-                       if (k != 3)\r
-                               continue;       // only test non-axial edges\r
-\r
-                       // try the six possible slanted axials from this edge\r
-                       for (axis=0 ; axis <3 ; axis++)\r
-                       {\r
-                               for (dir=-1 ; dir <= 1 ; dir+=2)\r
-                               {\r
-                                       // construct a plane\r
-                                       VectorClear (vec2);\r
-                                       vec2[axis] = dir;\r
-                                       CrossProduct (vec, vec2, normal);\r
-                                       if (VectorNormalize (normal, normal) < 0.5)\r
-                                               continue;\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<b->numsides ; k++)\r
-                                       {\r
-                                               // if this plane has allready been used, skip it\r
-                                               if (PlaneEqual (&mapplanes[b->original_sides[k].planenum]\r
-                                                       , normal, dist) )\r
-                                                       break;\r
-\r
-                                               w2 = b->original_sides[k].winding;\r
-                                               if (!w2)\r
-                                                       continue;\r
-                                               for (l=0 ; l<w2->numpoints ; l++)\r
-                                               {\r
-                                                       d = DotProduct (w2->p[l], normal) - dist;\r
-                                                       if (d > 0.1)\r
-                                                               break;  // point in front\r
-                                               }\r
-                                               if (l != w2->numpoints)\r
-                                                       break;\r
-                                       }\r
-\r
-                                       if (k != b->numsides)\r
-                                               continue;       // wasn't part of the outer hull\r
-                                       // add this plane\r
-                                       if (nummapbrushsides == MAX_MAP_BRUSHSIDES)\r
-                                               Error ("MAX_MAP_BRUSHSIDES");\r
-                                       nummapbrushsides++;\r
-                                       s2 = &b->original_sides[b->numsides];\r
-                                       s2->planenum = FindFloatPlane (normal, dist);\r
-                                       s2->texinfo = b->original_sides[0].texinfo;\r
-                                       s2->contents = b->original_sides[0].contents;\r
-                                       s2->bevel = true;\r
-                                       c_edgebevels++;\r
-                                       b->numsides++;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-\r
-/*\r
-================\r
-MakeBrushWindings\r
-\r
-makes basewindigs for sides and mins / maxs for the brush\r
-================\r
-*/\r
-qboolean MakeBrushWindings (mapbrush_t *ob)\r
-{\r
-       int                     i, j;\r
-       winding_t       *w;\r
-       side_t          *side;\r
-       plane_t         *plane;\r
-\r
-       ClearBounds (ob->mins, ob->maxs);\r
-\r
-       for (i=0 ; i<ob->numsides ; i++)\r
-       {\r
-               plane = &mapplanes[ob->original_sides[i].planenum];\r
-               w = BaseWindingForPlane (plane->normal, plane->dist);\r
-               for (j=0 ; j<ob->numsides && w; j++)\r
-               {\r
-                       if (i == j)\r
-                               continue;\r
-                       if (ob->original_sides[j].bevel)\r
-                               continue;\r
-                       plane = &mapplanes[ob->original_sides[j].planenum^1];\r
-                       ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);\r
-               }\r
-\r
-               side = &ob->original_sides[i];\r
-               side->winding = w;\r
-               if (w)\r
-               {\r
-                       side->visible = true;\r
-                       for (j=0 ; j<w->numpoints ; j++)\r
-                               AddPointToBounds (w->p[j], ob->mins, ob->maxs);\r
-               }\r
-       }\r
-\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               if (ob->mins[0] < -4096 || ob->maxs[0] > 4096)\r
-                       Sys_Printf ("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum);\r
-               if (ob->mins[0] > 4096 || ob->maxs[0] < -4096)\r
-                       Sys_Printf ("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum);\r
-       }\r
-\r
-       return true;\r
-}\r
-\r
-\r
-/*\r
-=================\r
-ParseBrush\r
-=================\r
-*/\r
-void ParseBrush (entity_t *mapent)\r
-{\r
-       mapbrush_t              *b;\r
-       int                     i,j, k;\r
-       int                     mt;\r
-       side_t          *side, *s2;\r
-       int                     planenum;\r
-       brush_texture_t td;\r
-       int                     planepts[3][3];\r
-\r
-       if (nummapbrushes == MAX_MAP_BRUSHES)\r
-               Error ("nummapbrushes == MAX_MAP_BRUSHES");\r
-\r
-       b = &mapbrushes[nummapbrushes];\r
-       b->original_sides = &brushsides[nummapbrushsides];\r
-       b->entitynum = num_entities-1;\r
-       b->brushnum = nummapbrushes - mapent->firstbrush;\r
-\r
-       do\r
-       {\r
-               if (!GetToken (true))\r
-                       break;\r
-               if (!strcmp (token, "}") )\r
-                       break;\r
-\r
-               if (nummapbrushsides == MAX_MAP_BRUSHSIDES)\r
-                       Error ("MAX_MAP_BRUSHSIDES");\r
-               side = &brushsides[nummapbrushsides];\r
-\r
-               // read the three point plane definition\r
-               for (i=0 ; i<3 ; i++)\r
-               {\r
-                       if (i != 0)\r
-                               GetToken (true);\r
-                       if (strcmp (token, "(") )\r
-                               Error ("parsing brush");\r
-                       \r
-                       for (j=0 ; j<3 ; j++)\r
-                       {\r
-                               GetToken (false);\r
-                               planepts[i][j] = atoi(token);\r
-                       }\r
-                       \r
-                       GetToken (false);\r
-                       if (strcmp (token, ")") )\r
-                               Error ("parsing brush");\r
-                               \r
-               }\r
-\r
-\r
-               //\r
-               // read the texturedef\r
-               //\r
-               GetToken (false);\r
-               strcpy (td.name, token);\r
-\r
-               GetToken (false);\r
-               td.shift[0] = atoi(token);\r
-               GetToken (false);\r
-               td.shift[1] = atoi(token);\r
-               GetToken (false);\r
-               td.rotate = atoi(token);        \r
-               GetToken (false);\r
-               td.scale[0] = atof(token);\r
-               GetToken (false);\r
-               td.scale[1] = atof(token);\r
-\r
-               // find default flags and values\r
-               mt = FindMiptex (td.name);\r
-               td.flags = textureref[mt].flags;\r
-               td.value = textureref[mt].value;\r
-               side->contents = textureref[mt].contents;\r
-               side->surf = td.flags = textureref[mt].flags;\r
-\r
-               if (TokenAvailable())\r
-               {\r
-                       GetToken (false);\r
-                       side->contents = atoi(token);\r
-                       GetToken (false);\r
-                       side->surf = td.flags = atoi(token);\r
-                       GetToken (false);\r
-                       td.value = atoi(token);\r
-               }\r
-\r
-               // translucent objects are automatically classified as detail\r
-               if (side->surf & (SURF_TRANS33|SURF_TRANS66) )\r
-                       side->contents |= CONTENTS_DETAIL;\r
-               if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )\r
-                       side->contents |= CONTENTS_DETAIL;\r
-               if (fulldetail)\r
-                       side->contents &= ~CONTENTS_DETAIL;\r
-               if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) \r
-                       | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST)  ) )\r
-                       side->contents |= CONTENTS_SOLID;\r
-\r
-               // hints and skips are never detail, and have no content\r
-               if (side->surf & (SURF_HINT|SURF_SKIP) )\r
-               {\r
-                       side->contents = 0;\r
-                       side->surf &= ~CONTENTS_DETAIL;\r
-               }\r
-\r
-\r
-               //\r
-               // find the plane number\r
-               //\r
-               planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]);\r
-               if (planenum == -1)\r
-               {\r
-                       Sys_Printf ("Entity %i, Brush %i: plane with no normal\n"\r
-                               , b->entitynum, b->brushnum);\r
-                       continue;\r
-               }\r
-\r
-               //\r
-               // see if the plane has been used already\r
-               //\r
-               for (k=0 ; k<b->numsides ; k++)\r
-               {\r
-                       s2 = b->original_sides + k;\r
-                       if (s2->planenum == planenum)\r
-                       {\r
-                               Sys_Printf ("Entity %i, Brush %i: duplicate plane\n"\r
-                                       , b->entitynum, b->brushnum);\r
-                               break;\r
-                       }\r
-                       if ( s2->planenum == (planenum^1) )\r
-                       {\r
-                               Sys_Printf ("Entity %i, Brush %i: mirrored plane\n"\r
-                                       , b->entitynum, b->brushnum);\r
-                               break;\r
-                       }\r
-               }\r
-               if (k != b->numsides)\r
-                       continue;               // duplicated\r
-\r
-               //\r
-               // keep this side\r
-               //\r
-\r
-               side = b->original_sides + b->numsides;\r
-               side->planenum = planenum;\r
-               side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum],\r
-                       &td, vec3_origin);\r
-\r
-               // save the td off in case there is an origin brush and we\r
-               // have to recalculate the texinfo\r
-               side_brushtextures[nummapbrushsides] = td;\r
-\r
-               nummapbrushsides++;\r
-               b->numsides++;\r
-       } while (1);\r
-\r
-       // get the content for the entire brush\r
-       b->contents = BrushContents (b);\r
-\r
-       // allow detail brushes to be removed \r
-       if (nodetail && (b->contents & CONTENTS_DETAIL) )\r
-       {\r
-               b->numsides = 0;\r
-               return;\r
-       }\r
-\r
-       // allow water brushes to be removed\r
-       if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )\r
-       {\r
-               b->numsides = 0;\r
-               return;\r
-       }\r
-\r
-       // create windings for sides and bounds for brush\r
-       MakeBrushWindings (b);\r
-\r
-       // brushes that will not be visible at all will never be\r
-       // used as bsp splitters\r
-       if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )\r
-       {\r
-               c_clipbrushes++;\r
-               for (i=0 ; i<b->numsides ; i++)\r
-                       b->original_sides[i].texinfo = TEXINFO_NODE;\r
-       }\r
-\r
-       //\r
-       // origin brushes are removed, but they set\r
-       // the rotation origin for the rest of the brushes\r
-       // in the entity.  After the entire entity is parsed,\r
-       // the planenums and texinfos will be adjusted for\r
-       // the origin brush\r
-       //\r
-       if (b->contents & CONTENTS_ORIGIN)\r
-       {\r
-               char    string[32];\r
-               vec3_t  origin;\r
-\r
-               if (num_entities == 1)\r
-               {\r
-                       Error ("Entity %i, Brush %i: origin brushes not allowed in world"\r
-                               , b->entitynum, b->brushnum);\r
-                       return;\r
-               }\r
-\r
-               VectorAdd (b->mins, b->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[b->entitynum], "origin", string);\r
-\r
-               VectorCopy (origin, entities[b->entitynum].origin);\r
-\r
-               // don't keep this brush\r
-               b->numsides = 0;\r
-\r
-               return;\r
-       }\r
-\r
-       AddBrushBevels (b);\r
-\r
-       nummapbrushes++;\r
-       mapent->numbrushes++;           \r
-}\r
-\r
-/*\r
-================\r
-MoveBrushesToWorld\r
-\r
-Takes all of the brushes from the current entity and\r
-adds them to the world's brush list.\r
-\r
-Used by func_group and func_areaportal\r
-================\r
-*/\r
-void MoveBrushesToWorld (entity_t *mapent)\r
-{\r
-       int                     newbrushes;\r
-       int                     worldbrushes;\r
-       mapbrush_t      *temp;\r
-       int                     i;\r
-\r
-       // this is pretty gross, because the brushes are expected to be\r
-       // in linear order for each entity\r
-\r
-       newbrushes = mapent->numbrushes;\r
-       worldbrushes = entities[0].numbrushes;\r
-\r
-       temp = malloc(newbrushes*sizeof(mapbrush_t));\r
-       memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));\r
-\r
-#if    0               // let them keep their original brush numbers\r
-       for (i=0 ; i<newbrushes ; i++)\r
-               temp[i].entitynum = 0;\r
-#endif\r
-\r
-       // make space to move the brushes (overlapped copy)\r
-       memmove (mapbrushes + worldbrushes + newbrushes,\r
-               mapbrushes + worldbrushes,\r
-               sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );\r
-\r
-       // copy the new brushes down\r
-       memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);\r
-\r
-       // fix up indexes\r
-       entities[0].numbrushes += newbrushes;\r
-       for (i=1 ; i<num_entities ; i++)\r
-               entities[i].firstbrush += newbrushes;\r
-       free (temp);\r
-\r
-       mapent->numbrushes = 0;\r
-}\r
-\r
-/*\r
-================\r
-ParseMapEntity\r
-================\r
-*/\r
-qboolean       ParseMapEntity (void)\r
-{\r
-       entity_t        *mapent;\r
-       epair_t         *e;\r
-       side_t          *s;\r
-       int                     i, j;\r
-       int                     startbrush, startsides;\r
-       vec_t           newdist;\r
-       mapbrush_t      *b;\r
-\r
-       if (!GetToken (true))\r
-               return false;\r
-\r
-       if (strcmp (token, "{") )\r
-               Error ("ParseEntity: { not found");\r
-       \r
-       if (num_entities == MAX_MAP_ENTITIES)\r
-               Error ("num_entities == MAX_MAP_ENTITIES");\r
-\r
-       startbrush = nummapbrushes;\r
-       startsides = nummapbrushsides;\r
-\r
-       mapent = &entities[num_entities];\r
-       num_entities++;\r
-       memset (mapent, 0, sizeof(*mapent));\r
-       mapent->firstbrush = nummapbrushes;\r
-       mapent->numbrushes = 0;\r
-//     mapent->portalareas[0] = -1;\r
-//     mapent->portalareas[1] = -1;\r
-\r
-       do\r
-       {\r
-               if (!GetToken (true))\r
-                       Error ("ParseEntity: EOF without closing brace");\r
-               if (!strcmp (token, "}") )\r
-                       break;\r
-               if (!strcmp (token, "{") )\r
-                       ParseBrush (mapent);\r
-               else\r
-               {\r
-                       e = ParseEpair ();\r
-                       e->next = mapent->epairs;\r
-                       mapent->epairs = e;\r
-               }\r
-       } while (1);\r
-\r
-       GetVectorForKey (mapent, "origin", mapent->origin);\r
-\r
-       //\r
-       // if there was an origin brush, offset all of the planes and texinfo\r
-       //\r
-       if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])\r
-       {\r
-               for (i=0 ; i<mapent->numbrushes ; i++)\r
-               {\r
-                       b = &mapbrushes[mapent->firstbrush + i];\r
-                       for (j=0 ; j<b->numsides ; j++)\r
-                       {\r
-                               s = &b->original_sides[j];\r
-                               newdist = mapplanes[s->planenum].dist -\r
-                                       DotProduct (mapplanes[s->planenum].normal, mapent->origin);\r
-                               s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);\r
-                               s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum],\r
-                                       &side_brushtextures[s-brushsides], mapent->origin);\r
-                       }\r
-                       MakeBrushWindings (b);\r
-               }\r
-       }\r
-\r
-       // group entities are just for editor convenience\r
-       // toss all brushes into the world entity\r
-       if (!strcmp ("func_group", ValueForKey (mapent, "classname")))\r
-       {\r
-               MoveBrushesToWorld (mapent);\r
-               mapent->numbrushes = 0;\r
-               return true;\r
-       }\r
-\r
-       // areaportal entities move their brushes, but don't eliminate\r
-       // the entity\r
-       if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))\r
-       {\r
-               char    str[128];\r
-\r
-               if (mapent->numbrushes != 1)\r
-                       Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);\r
-\r
-               b = &mapbrushes[nummapbrushes-1];\r
-               b->contents = CONTENTS_AREAPORTAL;\r
-               c_areaportals++;\r
-               mapent->areaportalnum = c_areaportals;\r
-               // set the portal number as "style"\r
-               sprintf (str, "%i", c_areaportals);\r
-               SetKeyValue (mapent, "style", str);\r
-               MoveBrushesToWorld (mapent);\r
-               return true;\r
-       }\r
-\r
-       return true;\r
-}\r
-\r
-//===================================================================\r
-\r
-/*\r
-================\r
-LoadMapFile\r
-================\r
-*/\r
-void LoadMapFile (char *filename)\r
-{              \r
-       int             i;\r
-\r
-       Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n");\r
-\r
-       LoadScriptFile (filename);\r
-\r
-       nummapbrushsides = 0;\r
-       num_entities = 0;\r
-       \r
-       while (ParseMapEntity ())\r
-       {\r
-       }\r
-\r
-       ClearBounds (map_mins, map_maxs);\r
-       for (i=0 ; i<entities[0].numbrushes ; i++)\r
-       {\r
-               if (mapbrushes[i].mins[0] > 4096)\r
-                       continue;       // no valid points\r
-               AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);\r
-               AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);\r
-       }\r
-\r
-       Sys_FPrintf( SYS_VRB, "%5i brushes\n", nummapbrushes);\r
-       Sys_FPrintf( SYS_VRB, "%5i clipbrushes\n", c_clipbrushes);\r
-       Sys_FPrintf( SYS_VRB, "%5i total sides\n", nummapbrushsides);\r
-       Sys_FPrintf( SYS_VRB, "%5i boxbevels\n", c_boxbevels);\r
-       Sys_FPrintf( SYS_VRB, "%5i edgebevels\n", c_edgebevels);\r
-       Sys_FPrintf( SYS_VRB, "%5i entities\n", num_entities);\r
-       Sys_FPrintf( SYS_VRB, "%5i planes\n", nummapplanes);\r
-       Sys_FPrintf( SYS_VRB, "%5i areaportals\n", c_areaportals);\r
-       Sys_FPrintf( SYS_VRB, "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],\r
-               map_maxs[0],map_maxs[1],map_maxs[2]);\r
-\r
-//     TestExpandBrushes ();\r
-}\r
-\r
-\r
-//====================================================================\r
-\r
-\r
-/*\r
-================\r
-TestExpandBrushes\r
-\r
-Expands all the brush planes and saves a new map out\r
-================\r
-*/\r
-void TestExpandBrushes (void)\r
-{\r
-       FILE    *f;\r
-       side_t  *s;\r
-       int             i, j, bn;\r
-       winding_t       *w;\r
-       char    *name = "expanded.map";\r
-       mapbrush_t      *brush;\r
-       vec_t   dist;\r
-\r
-       Sys_Printf ("writing %s\n", name);\r
-       f = fopen (name, "wb");\r
-       if (!f)\r
-               Error ("Can't write %s\b", name);\r
-\r
-       fprintf (f, "{\n\"classname\" \"worldspawn\"\n");\r
-\r
-       for (bn=0 ; bn<nummapbrushes ; bn++)\r
-       {\r
-               brush = &mapbrushes[bn];\r
-               fprintf (f, "{\n");\r
-               for (i=0 ; i<brush->numsides ; i++)\r
-               {\r
-                       s = brush->original_sides + i;\r
-                       dist = mapplanes[s->planenum].dist;\r
-                       for (j=0 ; j<3 ; j++)\r
-                               dist += fabs( 16 * mapplanes[s->planenum].normal[j] );\r
-\r
-                       w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist);\r
-\r
-                       fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);\r
-                       fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);\r
-                       fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);\r
-\r
-                       fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);\r
-                       FreeWinding (w);\r
-               }\r
-               fprintf (f, "}\n");\r
-       }\r
-       fprintf (f, "}\n");\r
-\r
-       fclose (f);\r
-\r
-       Error ("can't proceed after expanding brushes");\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
+ */
+// map.c
+
+#include "qbsp.h"
+
+extern qboolean onlyents;
+
+int nummapbrushes;
+mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+int nummapbrushsides;
+side_t brushsides[MAX_MAP_SIDES];
+brush_texture_t side_brushtextures[MAX_MAP_SIDES];
+
+int nummapplanes;
+plane_t mapplanes[MAX_MAP_PLANES];
+
+#define PLANE_HASHES    1024
+plane_t     *planehash[PLANE_HASHES];
+
+vec3_t map_mins, map_maxs;
+
+// undefine to make plane finding use linear sort
+#define USE_HASHING
+
+void TestExpandBrushes( void );
+
+int c_boxbevels;
+int c_edgebevels;
+
+int c_areaportals;
+
+int c_clipbrushes;
+
+/*
+   =============================================================================
+
+   PLANE FINDING
+
+   =============================================================================
+ */
+
+
+/*
+   =================
+   PlaneTypeForNormal
+   =================
+ */
+int PlaneTypeForNormal( vec3_t normal ){
+       vec_t ax, ay, az;
+
+// NOTE: should these have an epsilon around 1.0?
+       if ( normal[0] == 1.0 || normal[0] == -1.0 ) {
+               return PLANE_X;
+       }
+       if ( normal[1] == 1.0 || normal[1] == -1.0 ) {
+               return PLANE_Y;
+       }
+       if ( normal[2] == 1.0 || normal[2] == -1.0 ) {
+               return PLANE_Z;
+       }
+
+       ax = fabs( normal[0] );
+       ay = fabs( normal[1] );
+       az = fabs( normal[2] );
+
+       if ( ax >= ay && ax >= az ) {
+               return PLANE_ANYX;
+       }
+       if ( ay >= ax && ay >= az ) {
+               return PLANE_ANYY;
+       }
+       return PLANE_ANYZ;
+}
+
+/*
+   ================
+   PlaneEqual
+   ================
+ */
+#define NORMAL_EPSILON  0.00001
+#define DIST_EPSILON    0.01
+qboolean    PlaneEqual( plane_t *p, vec3_t normal, vec_t dist ){
+#if 1
+       if (
+               fabs( p->normal[0] - normal[0] ) < NORMAL_EPSILON
+               && fabs( p->normal[1] - normal[1] ) < NORMAL_EPSILON
+               && fabs( p->normal[2] - normal[2] ) < NORMAL_EPSILON
+               && fabs( p->dist - dist ) < DIST_EPSILON ) {
+               return true;
+       }
+#else
+       if ( p->normal[0] == normal[0]
+                && p->normal[1] == normal[1]
+                && p->normal[2] == normal[2]
+                && p->dist == dist ) {
+               return true;
+       }
+#endif
+       return false;
+}
+
+/*
+   ================
+   AddPlaneToHash
+   ================
+ */
+void    AddPlaneToHash( plane_t *p ){
+       int hash;
+
+       hash = (int)fabs( p->dist ) / 8;
+       hash &= ( PLANE_HASHES - 1 );
+
+       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 ) {
+               Error( "FloatPlane: bad normal" );
+       }
+       // 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;
+}
+
+/*
+   ==============
+   SnapVector
+   ==============
+ */
+void    SnapVector( vec3_t normal ){
+       int i;
+
+       for ( i = 0 ; i < 3 ; i++ )
+       {
+               if ( fabs( normal[i] - 1 ) < NORMAL_EPSILON ) {
+                       VectorClear( normal );
+                       normal[i] = 1;
+                       break;
+               }
+               if ( fabs( normal[i] - -1 ) < NORMAL_EPSILON ) {
+                       VectorClear( normal );
+                       normal[i] = -1;
+                       break;
+               }
+       }
+}
+
+/*
+   ==============
+   SnapPlane
+   ==============
+ */
+void    SnapPlane( vec3_t normal, vec_t *dist ){
+       SnapVector( normal );
+
+       if ( fabs( *dist - Q_rint( *dist ) ) < DIST_EPSILON ) {
+               *dist = Q_rint( *dist );
+       }
+}
+
+/*
+   =============
+   FindFloatPlane
+
+   =============
+ */
+#ifndef USE_HASHING
+int     FindFloatPlane( vec3_t normal, vec_t dist ){
+       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 );
+}
+#else
+int     FindFloatPlane( vec3_t normal, vec_t dist ){
+       int i;
+       plane_t *p;
+       int hash, h;
+
+       SnapPlane( normal, &dist );
+       hash = (int)fabs( dist ) / 8;
+       hash &= ( PLANE_HASHES - 1 );
+
+       // search the border bins as well
+       for ( i = -1 ; i <= 1 ; i++ )
+       {
+               h = ( hash + i ) & ( PLANE_HASHES - 1 );
+               for ( p = planehash[h] ; p ; p = p->hash_chain )
+               {
+                       if ( PlaneEqual( p, normal, dist ) ) {
+                               return p - mapplanes;
+                       }
+               }
+       }
+
+       return CreateNewFloatPlane( normal, dist );
+}
+#endif
+
+/*
+   ================
+   PlaneFromPoints
+   ================
+ */
+int PlaneFromPoints( int *p0, int *p1, int *p2 ){
+       vec3_t t1, t2, normal;
+       vec_t dist;
+
+       VectorSubtract( p0, p1, t1 );
+       VectorSubtract( p2, p1, t2 );
+       CrossProduct( t1, t2, normal );
+       VectorNormalize( normal, normal );
+
+       dist = DotProduct( p0, normal );
+
+       return FindFloatPlane( normal, dist );
+}
+
+
+//====================================================================
+
+
+/*
+   ===========
+   BrushContents
+   ===========
+ */
+int BrushContents( mapbrush_t *b ){
+       int contents;
+       side_t      *s;
+       int i;
+       int trans;
+
+       s = &b->original_sides[0];
+       contents = s->contents;
+       trans = texinfo[s->texinfo].flags;
+       for ( i = 1 ; i < b->numsides ; i++, s++ )
+       {
+               s = &b->original_sides[i];
+               trans |= texinfo[s->texinfo].flags;
+               if ( s->contents != contents ) {
+                       Sys_Printf( "Entity %i, Brush %i: mixed face contents\n"
+                                               , b->entitynum, b->brushnum );
+                       break;
+               }
+       }
+
+       // if any side is translucent, mark the contents
+       // and change solid to window
+       if ( trans & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
+               contents |= CONTENTS_TRANSLUCENT;
+               if ( contents & CONTENTS_SOLID ) {
+                       contents &= ~CONTENTS_SOLID;
+                       contents |= CONTENTS_WINDOW;
+               }
+       }
+
+       return contents;
+}
+
+
+//============================================================================
+
+/*
+   =================
+   AddBrushBevels
+
+   Adds any additional planes necessary to allow the brush to be expanded
+   against axial bounding boxes
+   =================
+ */
+void AddBrushBevels( mapbrush_t *b ){
+       int axis, dir;
+       int i, j, k, l, order;
+       side_t sidetemp;
+       brush_texture_t tdtemp;
+       side_t  *s, *s2;
+       vec3_t normal;
+       float dist;
+       winding_t   *w, *w2;
+       vec3_t vec, vec2;
+       float d;
+
+       //
+       // 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 = b->original_sides ; i < b->numsides ; i++,s++ )
+                       {
+                               if ( mapplanes[s->planenum].normal[axis] == dir ) {
+                                       break;
+                               }
+                       }
+
+                       if ( i == b->numsides ) { // add a new side
+                               if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
+                                       Error( "MAX_MAP_BRUSHSIDES" );
+                               }
+                               nummapbrushsides++;
+                               b->numsides++;
+                               VectorClear( normal );
+                               normal[axis] = dir;
+                               if ( dir == 1 ) {
+                                       dist = b->maxs[axis];
+                               }
+                               else{
+                                       dist = -b->mins[axis];
+                               }
+                               s->planenum = FindFloatPlane( normal, dist );
+                               s->texinfo = b->original_sides[0].texinfo;
+                               s->contents = b->original_sides[0].contents;
+                               s->bevel = true;
+                               c_boxbevels++;
+                       }
+
+                       // if the plane is not in it canonical order, swap it
+                       if ( i != order ) {
+                               sidetemp = b->original_sides[order];
+                               b->original_sides[order] = b->original_sides[i];
+                               b->original_sides[i] = sidetemp;
+
+                               j = b->original_sides - brushsides;
+                               tdtemp = side_brushtextures[j + order];
+                               side_brushtextures[j + order] = side_brushtextures[j + i];
+                               side_brushtextures[j + i] = tdtemp;
+                       }
+               }
+       }
+
+       //
+       // add the edge bevels
+       //
+       if ( b->numsides == 6 ) {
+               return;     // pure axial
+
+       }
+       // test the non-axial plane edges
+       for ( i = 6 ; i < b->numsides ; i++ )
+       {
+               s = b->original_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.5 ) {
+                               continue;
+                       }
+                       SnapVector( vec );
+                       for ( k = 0 ; k < 3 ; k++ )
+                               if ( vec[k] == -1 || vec[k] == 1 ) {
+                                       break;
+                               }           // axial
+                       if ( k != 3 ) {
+                               continue;   // only test non-axial edges
+
+                       }
+                       // 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.5 ) {
+                                               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 < b->numsides ; k++ )
+                                       {
+                                               // if this plane has allready been used, skip it
+                                               if ( PlaneEqual( &mapplanes[b->original_sides[k].planenum]
+                                                                                , normal, dist ) ) {
+                                                       break;
+                                               }
+
+                                               w2 = b->original_sides[k].winding;
+                                               if ( !w2 ) {
+                                                       continue;
+                                               }
+                                               for ( l = 0 ; l < w2->numpoints ; l++ )
+                                               {
+                                                       d = DotProduct( w2->p[l], normal ) - dist;
+                                                       if ( d > 0.1 ) {
+                                                               break;  // point in front
+                                                       }
+                                               }
+                                               if ( l != w2->numpoints ) {
+                                                       break;
+                                               }
+                                       }
+
+                                       if ( k != b->numsides ) {
+                                               continue;   // wasn't part of the outer hull
+                                       }
+                                       // add this plane
+                                       if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
+                                               Error( "MAX_MAP_BRUSHSIDES" );
+                                       }
+                                       nummapbrushsides++;
+                                       s2 = &b->original_sides[b->numsides];
+                                       s2->planenum = FindFloatPlane( normal, dist );
+                                       s2->texinfo = b->original_sides[0].texinfo;
+                                       s2->contents = b->original_sides[0].contents;
+                                       s2->bevel = true;
+                                       c_edgebevels++;
+                                       b->numsides++;
+                               }
+                       }
+               }
+       }
+}
+
+
+/*
+   ================
+   MakeBrushWindings
+
+   makes basewindigs for sides and mins / maxs for the brush
+   ================
+ */
+qboolean MakeBrushWindings( mapbrush_t *ob ){
+       int i, j;
+       winding_t   *w;
+       side_t      *side;
+       plane_t     *plane;
+
+       ClearBounds( ob->mins, ob->maxs );
+
+       for ( i = 0 ; i < ob->numsides ; i++ )
+       {
+               plane = &mapplanes[ob->original_sides[i].planenum];
+               w = BaseWindingForPlane( plane->normal, plane->dist );
+               for ( j = 0 ; j < ob->numsides && w; j++ )
+               {
+                       if ( i == j ) {
+                               continue;
+                       }
+                       if ( ob->original_sides[j].bevel ) {
+                               continue;
+                       }
+                       plane = &mapplanes[ob->original_sides[j].planenum ^ 1];
+                       ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON);
+               }
+
+               side = &ob->original_sides[i];
+               side->winding = w;
+               if ( w ) {
+                       side->visible = true;
+                       for ( j = 0 ; j < w->numpoints ; j++ )
+                               AddPointToBounds( w->p[j], ob->mins, ob->maxs );
+               }
+       }
+
+       for ( i = 0 ; i < 3 ; i++ )
+       {
+               if ( ob->mins[0] < -4096 || ob->maxs[0] > 4096 ) {
+                       Sys_Printf( "entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum );
+               }
+               if ( ob->mins[0] > 4096 || ob->maxs[0] < -4096 ) {
+                       Sys_Printf( "entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum );
+               }
+       }
+
+       return true;
+}
+
+
+/*
+   =================
+   ParseBrush
+   =================
+ */
+void ParseBrush( entity_t *mapent ){
+       mapbrush_t      *b;
+       int i,j, k;
+       int mt;
+       side_t      *side, *s2;
+       int planenum;
+       brush_texture_t td;
+       int planepts[3][3];
+
+       if ( nummapbrushes == MAX_MAP_BRUSHES ) {
+               Error( "nummapbrushes == MAX_MAP_BRUSHES" );
+       }
+
+       b = &mapbrushes[nummapbrushes];
+       b->original_sides = &brushsides[nummapbrushsides];
+       b->entitynum = num_entities - 1;
+       b->brushnum = nummapbrushes - mapent->firstbrush;
+
+       do
+       {
+               if ( !GetToken( true ) ) {
+                       break;
+               }
+               if ( !strcmp( token, "}" ) ) {
+                       break;
+               }
+
+               if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
+                       Error( "MAX_MAP_BRUSHSIDES" );
+               }
+               side = &brushsides[nummapbrushsides];
+
+               // read the three point plane definition
+               for ( i = 0 ; i < 3 ; i++ )
+               {
+                       if ( i != 0 ) {
+                               GetToken( true );
+                       }
+                       if ( strcmp( token, "(" ) ) {
+                               Error( "parsing brush" );
+                       }
+
+                       for ( j = 0 ; j < 3 ; j++ )
+                       {
+                               GetToken( false );
+                               planepts[i][j] = atoi( token );
+                       }
+
+                       GetToken( false );
+                       if ( strcmp( token, ")" ) ) {
+                               Error( "parsing brush" );
+                       }
+
+               }
+
+
+               //
+               // read the texturedef
+               //
+               GetToken( false );
+               strcpy( td.name, token );
+
+               GetToken( false );
+               td.shift[0] = atoi( token );
+               GetToken( false );
+               td.shift[1] = atoi( token );
+               GetToken( false );
+               td.rotate = atoi( token );
+               GetToken( false );
+               td.scale[0] = atof( token );
+               GetToken( false );
+               td.scale[1] = atof( token );
+
+               // find default flags and values
+               mt = FindMiptex( td.name );
+               td.flags = textureref[mt].flags;
+               td.value = textureref[mt].value;
+               side->contents = textureref[mt].contents;
+               side->surf = td.flags = textureref[mt].flags;
+
+               if ( TokenAvailable() ) {
+                       GetToken( false );
+                       side->contents = atoi( token );
+                       GetToken( false );
+                       side->surf = td.flags = atoi( token );
+                       GetToken( false );
+                       td.value = atoi( token );
+               }
+
+               // translucent objects are automatically classified as detail
+               if ( side->surf & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
+                       side->contents |= CONTENTS_DETAIL;
+               }
+               if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
+                       side->contents |= CONTENTS_DETAIL;
+               }
+               if ( fulldetail ) {
+                       side->contents &= ~CONTENTS_DETAIL;
+               }
+               if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 )
+                                                                  | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST )  ) ) {
+                       side->contents |= CONTENTS_SOLID;
+               }
+
+               // hints and skips are never detail, and have no content
+               if ( side->surf & ( SURF_HINT | SURF_SKIP ) ) {
+                       side->contents = 0;
+                       side->surf &= ~CONTENTS_DETAIL;
+               }
+
+
+               //
+               // find the plane number
+               //
+               planenum = PlaneFromPoints( planepts[0], planepts[1], planepts[2] );
+               if ( planenum == -1 ) {
+                       Sys_Printf( "Entity %i, Brush %i: plane with no normal\n"
+                                               , b->entitynum, b->brushnum );
+                       continue;
+               }
+
+               //
+               // see if the plane has been used already
+               //
+               for ( k = 0 ; k < b->numsides ; k++ )
+               {
+                       s2 = b->original_sides + k;
+                       if ( s2->planenum == planenum ) {
+                               Sys_Printf( "Entity %i, Brush %i: duplicate plane\n"
+                                                       , b->entitynum, b->brushnum );
+                               break;
+                       }
+                       if ( s2->planenum == ( planenum ^ 1 ) ) {
+                               Sys_Printf( "Entity %i, Brush %i: mirrored plane\n"
+                                                       , b->entitynum, b->brushnum );
+                               break;
+                       }
+               }
+               if ( k != b->numsides ) {
+                       continue;       // duplicated
+
+               }
+               //
+               // keep this side
+               //
+
+               side = b->original_sides + b->numsides;
+               side->planenum = planenum;
+               side->texinfo = TexinfoForBrushTexture( &mapplanes[planenum],
+                                                                                               &td, vec3_origin );
+
+               // save the td off in case there is an origin brush and we
+               // have to recalculate the texinfo
+               side_brushtextures[nummapbrushsides] = td;
+
+               nummapbrushsides++;
+               b->numsides++;
+       } while ( 1 );
+
+       // get the content for the entire brush
+       b->contents = BrushContents( b );
+
+       // allow detail brushes to be removed
+       if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) {
+               b->numsides = 0;
+               return;
+       }
+
+       // allow water brushes to be removed
+       if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
+               b->numsides = 0;
+               return;
+       }
+
+       // create windings for sides and bounds for brush
+       MakeBrushWindings( b );
+
+       // brushes that will not be visible at all will never be
+       // used as bsp splitters
+       if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
+               c_clipbrushes++;
+               for ( i = 0 ; i < b->numsides ; i++ )
+                       b->original_sides[i].texinfo = TEXINFO_NODE;
+       }
+
+       //
+       // 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 ( b->contents & CONTENTS_ORIGIN ) {
+               char string[32];
+               vec3_t origin;
+
+               if ( num_entities == 1 ) {
+                       Error( "Entity %i, Brush %i: origin brushes not allowed in world"
+                                  , b->entitynum, b->brushnum );
+                       return;
+               }
+
+               VectorAdd( b->mins, b->maxs, origin );
+               VectorScale( origin, 0.5, origin );
+
+               sprintf( string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] );
+               SetKeyValue( &entities[b->entitynum], "origin", string );
+
+               VectorCopy( origin, entities[b->entitynum].origin );
+
+               // don't keep this brush
+               b->numsides = 0;
+
+               return;
+       }
+
+       AddBrushBevels( b );
+
+       nummapbrushes++;
+       mapent->numbrushes++;
+}
+
+/*
+   ================
+   MoveBrushesToWorld
+
+   Takes all of the brushes from the current entity and
+   adds them to the world's brush list.
+
+   Used by func_group and func_areaportal
+   ================
+ */
+void MoveBrushesToWorld( entity_t *mapent ){
+       int newbrushes;
+       int worldbrushes;
+       mapbrush_t  *temp;
+       int i;
+
+       // this is pretty gross, because the brushes are expected to be
+       // in linear order for each entity
+
+       newbrushes = mapent->numbrushes;
+       worldbrushes = entities[0].numbrushes;
+
+       temp = malloc( newbrushes * sizeof( mapbrush_t ) );
+       memcpy( temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof( mapbrush_t ) );
+
+#if 0       // let them keep their original brush numbers
+       for ( i = 0 ; i < newbrushes ; i++ )
+               temp[i].entitynum = 0;
+#endif
+
+       // make space to move the brushes (overlapped copy)
+       memmove( mapbrushes + worldbrushes + newbrushes,
+                        mapbrushes + worldbrushes,
+                        sizeof( mapbrush_t ) * ( nummapbrushes - worldbrushes - newbrushes ) );
+
+       // copy the new brushes down
+       memcpy( mapbrushes + worldbrushes, temp, sizeof( mapbrush_t ) * newbrushes );
+
+       // fix up indexes
+       entities[0].numbrushes += newbrushes;
+       for ( i = 1 ; i < num_entities ; i++ )
+               entities[i].firstbrush += newbrushes;
+       free( temp );
+
+       mapent->numbrushes = 0;
+}
+
+/*
+   ================
+   ParseMapEntity
+   ================
+ */
+qboolean    ParseMapEntity( void ){
+       entity_t    *mapent;
+       epair_t     *e;
+       side_t      *s;
+       int i, j;
+       int startbrush, startsides;
+       vec_t newdist;
+       mapbrush_t  *b;
+
+       if ( !GetToken( true ) ) {
+               return false;
+       }
+
+       if ( strcmp( token, "{" ) ) {
+               Error( "ParseEntity: { not found" );
+       }
+
+       if ( num_entities == MAX_MAP_ENTITIES ) {
+               Error( "num_entities == MAX_MAP_ENTITIES" );
+       }
+
+       startbrush = nummapbrushes;
+       startsides = nummapbrushsides;
+
+       mapent = &entities[num_entities];
+       num_entities++;
+       memset( mapent, 0, sizeof( *mapent ) );
+       mapent->firstbrush = nummapbrushes;
+       mapent->numbrushes = 0;
+//     mapent->portalareas[0] = -1;
+//     mapent->portalareas[1] = -1;
+
+       do
+       {
+               if ( !GetToken( true ) ) {
+                       Error( "ParseEntity: EOF without closing brace" );
+               }
+               if ( !strcmp( token, "}" ) ) {
+                       break;
+               }
+               if ( !strcmp( token, "{" ) ) {
+                       ParseBrush( mapent );
+               }
+               else
+               {
+                       e = ParseEpair();
+                       e->next = mapent->epairs;
+                       mapent->epairs = e;
+               }
+       } while ( 1 );
+
+       GetVectorForKey( mapent, "origin", mapent->origin );
+
+       //
+       // if there was an origin brush, offset all of the planes and texinfo
+       //
+       if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) {
+               for ( i = 0 ; i < mapent->numbrushes ; i++ )
+               {
+                       b = &mapbrushes[mapent->firstbrush + i];
+                       for ( j = 0 ; j < b->numsides ; j++ )
+                       {
+                               s = &b->original_sides[j];
+                               newdist = mapplanes[s->planenum].dist -
+                                                 DotProduct( mapplanes[s->planenum].normal, mapent->origin );
+                               s->planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist );
+                               s->texinfo = TexinfoForBrushTexture( &mapplanes[s->planenum],
+                                                                                                        &side_brushtextures[s - brushsides], mapent->origin );
+                       }
+                       MakeBrushWindings( b );
+               }
+       }
+
+       // group entities are just for editor convenience
+       // toss all brushes into the world entity
+       if ( !strcmp( "func_group", ValueForKey( mapent, "classname" ) ) ) {
+               MoveBrushesToWorld( mapent );
+               mapent->numbrushes = 0;
+               return true;
+       }
+
+       // areaportal entities move their brushes, but don't eliminate
+       // the entity
+       if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) {
+               char str[128];
+
+               if ( mapent->numbrushes != 1 ) {
+                       Error( "Entity %i: func_areaportal can only be a single brush", num_entities - 1 );
+               }
+
+               b = &mapbrushes[nummapbrushes - 1];
+               b->contents = CONTENTS_AREAPORTAL;
+               c_areaportals++;
+               mapent->areaportalnum = c_areaportals;
+               // set the portal number as "style"
+               sprintf( str, "%i", c_areaportals );
+               SetKeyValue( mapent, "style", str );
+               MoveBrushesToWorld( mapent );
+               return true;
+       }
+
+       return true;
+}
+
+//===================================================================
+
+/*
+   ================
+   LoadMapFile
+   ================
+ */
+void LoadMapFile( char *filename ){
+       int i;
+
+       Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
+
+       LoadScriptFile( filename );
+
+       nummapbrushsides = 0;
+       num_entities = 0;
+
+       while ( ParseMapEntity() )
+       {
+       }
+
+       ClearBounds( map_mins, map_maxs );
+       for ( i = 0 ; i < entities[0].numbrushes ; i++ )
+       {
+               if ( mapbrushes[i].mins[0] > 4096 ) {
+                       continue;   // no valid points
+               }
+               AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs );
+               AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs );
+       }
+
+       Sys_FPrintf( SYS_VRB, "%5i brushes\n", nummapbrushes );
+       Sys_FPrintf( SYS_VRB, "%5i clipbrushes\n", c_clipbrushes );
+       Sys_FPrintf( SYS_VRB, "%5i total sides\n", nummapbrushsides );
+       Sys_FPrintf( SYS_VRB, "%5i boxbevels\n", c_boxbevels );
+       Sys_FPrintf( SYS_VRB, "%5i edgebevels\n", c_edgebevels );
+       Sys_FPrintf( SYS_VRB, "%5i entities\n", num_entities );
+       Sys_FPrintf( SYS_VRB, "%5i planes\n", nummapplanes );
+       Sys_FPrintf( SYS_VRB, "%5i areaportals\n", c_areaportals );
+       Sys_FPrintf( SYS_VRB, "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
+                                map_maxs[0],map_maxs[1],map_maxs[2] );
+
+//     TestExpandBrushes ();
+}
+
+
+//====================================================================
+
+
+/*
+   ================
+   TestExpandBrushes
+
+   Expands all the brush planes and saves a new map out
+   ================
+ */
+void TestExpandBrushes( void ){
+       FILE    *f;
+       side_t  *s;
+       int i, j, bn;
+       winding_t   *w;
+       char    *name = "expanded.map";
+       mapbrush_t  *brush;
+       vec_t dist;
+
+       Sys_Printf( "writing %s\n", name );
+       f = fopen( name, "wb" );
+       if ( !f ) {
+               Error( "Can't write %s\b", name );
+       }
+
+       fprintf( f, "{\n\"classname\" \"worldspawn\"\n" );
+
+       for ( bn = 0 ; bn < nummapbrushes ; bn++ )
+       {
+               brush = &mapbrushes[bn];
+               fprintf( f, "{\n" );
+               for ( i = 0 ; i < brush->numsides ; i++ )
+               {
+                       s = brush->original_sides + i;
+                       dist = mapplanes[s->planenum].dist;
+                       for ( j = 0 ; j < 3 ; j++ )
+                               dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
+
+                       w = BaseWindingForPlane( mapplanes[s->planenum].normal, dist );
+
+                       fprintf( f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] );
+                       fprintf( f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] );
+                       fprintf( f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] );
+
+                       fprintf( f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture );
+                       FreeWinding( w );
+               }
+               fprintf( f, "}\n" );
+       }
+       fprintf( f, "}\n" );
+
+       fclose( f );
+
+       Error( "can't proceed after expanding brushes" );
+}