]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/q2map/faces.c
set eol-style
[xonotic/netradiant.git] / tools / quake2 / q2map / faces.c
index 0b7ba80fb448b12b05de7cd7648c1e34d2fd86da..4839f9c0912c5e10da905985dea3458cdcdd87ff 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
-// faces.c\r
-\r
-#include "qbsp.h"\r
-\r
-/*\r
-\r
-  some faces will be removed before saving, but still form nodes:\r
-\r
-  the insides of sky volumes\r
-  meeting planes of different water current volumes\r
-\r
-*/\r
-\r
-// undefine for dumb linear searches\r
-#define        USE_HASHING\r
-\r
-#define        INTEGRAL_EPSILON        0.01\r
-#define        POINT_EPSILON           0.5\r
-#define        OFF_EPSILON                     0.5\r
-\r
-int    c_merge;\r
-int    c_subdivide;\r
-\r
-int    c_totalverts;\r
-int    c_uniqueverts;\r
-int    c_degenerate;\r
-int    c_tjunctions;\r
-int    c_faceoverflows;\r
-int    c_facecollapse;\r
-int    c_badstartverts;\r
-\r
-#define        MAX_SUPERVERTS  512\r
-int    superverts[MAX_SUPERVERTS];\r
-int    numsuperverts;\r
-\r
-face_t         *edgefaces[MAX_MAP_EDGES][2];\r
-int            firstmodeledge = 1;\r
-int            firstmodelface;\r
-\r
-int    c_tryedges;\r
-\r
-vec3_t edge_dir;\r
-vec3_t edge_start;\r
-vec_t  edge_len;\r
-\r
-int            num_edge_verts;\r
-int            edge_verts[MAX_MAP_VERTS];\r
-\r
-\r
-float  subdivide_size = 240;\r
-\r
-\r
-face_t *NewFaceFromFace (face_t *f);\r
-\r
-//===========================================================================\r
-\r
-typedef struct hashvert_s\r
-{\r
-       struct hashvert_s       *next;\r
-       int             num;\r
-} hashvert_t;\r
-\r
-\r
-#define        HASH_SIZE       64\r
-\r
-\r
-int    vertexchain[MAX_MAP_VERTS];             // the next vertex in a hash chain\r
-int    hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts\r
-\r
-face_t         *edgefaces[MAX_MAP_EDGES][2];\r
-\r
-//============================================================================\r
-\r
-\r
-unsigned HashVec (vec3_t vec)\r
-{\r
-       int                     x, y;\r
-\r
-       x = (4096 + (int)(vec[0]+0.5)) >> 7;\r
-       y = (4096 + (int)(vec[1]+0.5)) >> 7;\r
-\r
-       if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )\r
-               Error ("HashVec: point outside valid range");\r
-       \r
-       return y*HASH_SIZE + x;\r
-}\r
-\r
-#ifdef USE_HASHING\r
-/*\r
-=============\r
-GetVertex\r
-\r
-Uses hashing\r
-=============\r
-*/\r
-int    GetVertexnum (vec3_t in)\r
-{\r
-       int                     h;\r
-       int                     i;\r
-       float           *p;\r
-       vec3_t          vert;\r
-       int                     vnum;\r
-\r
-       c_totalverts++;\r
-\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               if ( fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON)\r
-                       vert[i] = Q_rint(in[i]);\r
-               else\r
-                       vert[i] = in[i];\r
-       }\r
-       \r
-       h = HashVec (vert);\r
-       \r
-       for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])\r
-       {\r
-               p = dvertexes[vnum].point;\r
-               if ( fabs(p[0]-vert[0])<POINT_EPSILON\r
-               && fabs(p[1]-vert[1])<POINT_EPSILON\r
-               && fabs(p[2]-vert[2])<POINT_EPSILON )\r
-                       return vnum;\r
-       }\r
-       \r
-// emit a vertex\r
-       if (numvertexes == MAX_MAP_VERTS)\r
-               Error ("numvertexes == MAX_MAP_VERTS");\r
-\r
-       dvertexes[numvertexes].point[0] = vert[0];\r
-       dvertexes[numvertexes].point[1] = vert[1];\r
-       dvertexes[numvertexes].point[2] = vert[2];\r
-\r
-       vertexchain[numvertexes] = hashverts[h];\r
-       hashverts[h] = numvertexes;\r
-\r
-       c_uniqueverts++;\r
-\r
-       numvertexes++;\r
-               \r
-       return numvertexes-1;\r
-}\r
-#else\r
-/*\r
-==================\r
-GetVertexnum\r
-\r
-Dumb linear search\r
-==================\r
-*/\r
-int    GetVertexnum (vec3_t v)\r
-{\r
-       int                     i, j;\r
-       dvertex_t       *dv;\r
-       vec_t           d;\r
-\r
-       c_totalverts++;\r
-\r
-       // make really close values exactly integral\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )\r
-                       v[i] = (int)(v[i]+0.5);\r
-               if (v[i] < -4096 || v[i] > 4096)\r
-                       Error ("GetVertexnum: outside +/- 4096");\r
-       }\r
-\r
-       // search for an existing vertex match\r
-       for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)\r
-       {\r
-               for (j=0 ; j<3 ; j++)\r
-               {\r
-                       d = v[j] - dv->point[j];\r
-                       if ( d > POINT_EPSILON || d < -POINT_EPSILON)\r
-                               break;\r
-               }\r
-               if (j == 3)\r
-                       return i;               // a match\r
-       }\r
-\r
-       // new point\r
-       if (numvertexes == MAX_MAP_VERTS)\r
-               Error ("MAX_MAP_VERTS");\r
-       VectorCopy (v, dv->point);\r
-       numvertexes++;\r
-       c_uniqueverts++;\r
-\r
-       return numvertexes-1;\r
-}\r
-#endif\r
-\r
-\r
-/*\r
-==================\r
-FaceFromSuperverts\r
-\r
-The faces vertexes have beeb added to the superverts[] array,\r
-and there may be more there than can be held in a face (MAXEDGES).\r
-\r
-If less, the faces vertexnums[] will be filled in, otherwise\r
-face will reference a tree of split[] faces until all of the\r
-vertexnums can be added.\r
-\r
-superverts[base] will become face->vertexnums[0], and the others\r
-will be circularly filled in.\r
-==================\r
-*/\r
-void FaceFromSuperverts (node_t *node, face_t *f, int base)\r
-{\r
-       face_t  *newf;\r
-       int             remaining;\r
-       int             i;\r
-\r
-       remaining = numsuperverts;\r
-       while (remaining > MAXEDGES)\r
-       {       // must split into two faces, because of vertex overload\r
-               c_faceoverflows++;\r
-\r
-               newf = f->split[0] = NewFaceFromFace (f);\r
-               newf = f->split[0];\r
-               newf->next = node->faces;\r
-               node->faces = newf;\r
-\r
-               newf->numpoints = MAXEDGES;\r
-               for (i=0 ; i<MAXEDGES ; i++)\r
-                       newf->vertexnums[i] = superverts[(i+base)%numsuperverts];\r
-\r
-               f->split[1] = NewFaceFromFace (f);\r
-               f = f->split[1];\r
-               f->next = node->faces;\r
-               node->faces = f;\r
-\r
-               remaining -= (MAXEDGES-2);\r
-               base = (base+MAXEDGES-1)%numsuperverts;\r
-       }\r
-\r
-       // copy the vertexes back to the face\r
-       f->numpoints = remaining;\r
-       for (i=0 ; i<remaining ; i++)\r
-               f->vertexnums[i] = superverts[(i+base)%numsuperverts];\r
-}\r
-\r
-\r
-/*\r
-==================\r
-EmitFaceVertexes\r
-==================\r
-*/\r
-void EmitFaceVertexes (node_t *node, face_t *f)\r
-{\r
-       winding_t       *w;\r
-       int                     i;\r
-\r
-       if (f->merged || f->split[0] || f->split[1])\r
-               return;\r
-\r
-       w = f->w;\r
-       for (i=0 ; i<w->numpoints ; i++)\r
-       {\r
-               if (noweld)\r
-               {       // make every point unique\r
-                       if (numvertexes == MAX_MAP_VERTS)\r
-                               Error ("MAX_MAP_VERTS");\r
-                       superverts[i] = numvertexes;\r
-                       VectorCopy (w->p[i], dvertexes[numvertexes].point);\r
-                       numvertexes++;\r
-                       c_uniqueverts++;\r
-                       c_totalverts++;\r
-               }\r
-               else\r
-                       superverts[i] = GetVertexnum (w->p[i]);\r
-       }\r
-       numsuperverts = w->numpoints;\r
-\r
-       // this may fragment the face if > MAXEDGES\r
-       FaceFromSuperverts (node, f, 0);\r
-}\r
-\r
-/*\r
-==================\r
-EmitVertexes_r\r
-==================\r
-*/\r
-void EmitVertexes_r (node_t *node)\r
-{\r
-       int             i;\r
-       face_t  *f;\r
-\r
-       if (node->planenum == PLANENUM_LEAF)\r
-               return;\r
-\r
-       for (f=node->faces ; f ; f=f->next)\r
-       {\r
-               EmitFaceVertexes (node, f);\r
-       }\r
-\r
-       for (i=0 ; i<2 ; i++)\r
-               EmitVertexes_r (node->children[i]);\r
-}\r
-\r
-\r
-#ifdef USE_HASHING\r
-/*\r
-==========\r
-FindEdgeVerts\r
-\r
-Uses the hash tables to cut down to a small number\r
-==========\r
-*/\r
-void FindEdgeVerts (vec3_t v1, vec3_t v2)\r
-{\r
-       int             x1, x2, y1, y2, t;\r
-       int             x, y;\r
-       int             vnum;\r
-\r
-#if 0\r
-{\r
-       int             i;\r
-       num_edge_verts = numvertexes-1;\r
-       for (i=0 ; i<numvertexes-1 ; i++)\r
-               edge_verts[i] = i+1;\r
-}\r
-#endif\r
-\r
-       x1 = (4096 + (int)(v1[0]+0.5)) >> 7;\r
-       y1 = (4096 + (int)(v1[1]+0.5)) >> 7;\r
-       x2 = (4096 + (int)(v2[0]+0.5)) >> 7;\r
-       y2 = (4096 + (int)(v2[1]+0.5)) >> 7;\r
-\r
-       if (x1 > x2)\r
-       {\r
-               t = x1;\r
-               x1 = x2;\r
-               x2 = t;\r
-       }\r
-       if (y1 > y2)\r
-       {\r
-               t = y1;\r
-               y1 = y2;\r
-               y2 = t;\r
-       }\r
-#if 0\r
-       x1--;\r
-       x2++;\r
-       y1--;\r
-       y2++;\r
-       if (x1 < 0)\r
-               x1 = 0;\r
-       if (x2 >= HASH_SIZE)\r
-               x2 = HASH_SIZE;\r
-       if (y1 < 0)\r
-               y1 = 0;\r
-       if (y2 >= HASH_SIZE)\r
-               y2 = HASH_SIZE;\r
-#endif\r
-       num_edge_verts = 0;\r
-       for (x=x1 ; x <= x2 ; x++)\r
-       {\r
-               for (y=y1 ; y <= y2 ; y++)\r
-               {\r
-                       for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])\r
-                       {\r
-                               edge_verts[num_edge_verts++] = vnum;\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-#else\r
-/*\r
-==========\r
-FindEdgeVerts\r
-\r
-Forced a dumb check of everything\r
-==========\r
-*/\r
-void FindEdgeVerts (vec3_t v1, vec3_t v2)\r
-{\r
-       int             i;\r
-\r
-       num_edge_verts = numvertexes-1;\r
-       for (i=0 ; i<num_edge_verts ; i++)\r
-               edge_verts[i] = i+1;\r
-}\r
-#endif\r
-\r
-/*\r
-==========\r
-TestEdge\r
-\r
-Can be recursively reentered\r
-==========\r
-*/\r
-void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)\r
-{\r
-       int             j, k;\r
-       vec_t   dist;\r
-       vec3_t  delta;\r
-       vec3_t  exact;\r
-       vec3_t  off;\r
-       vec_t   error;\r
-       vec3_t  p;\r
-\r
-       if (p1 == p2)\r
-       {\r
-               c_degenerate++;\r
-               return;         // degenerate edge\r
-       }\r
-\r
-       for (k=startvert ; k<num_edge_verts ; k++)\r
-       {\r
-               j = edge_verts[k];\r
-               if (j==p1 || j == p2)\r
-                       continue;\r
-\r
-               VectorCopy (dvertexes[j].point, p);\r
-\r
-               VectorSubtract (p, edge_start, delta);\r
-               dist = DotProduct (delta, edge_dir);\r
-               if (dist <=start || dist >= end)\r
-                       continue;               // off an end\r
-               VectorMA (edge_start, dist, edge_dir, exact);\r
-               VectorSubtract (p, exact, off);\r
-               error = VectorLength (off);\r
-\r
-               if (fabs(error) > OFF_EPSILON)\r
-                       continue;               // not on the edge\r
-\r
-               // break the edge\r
-               c_tjunctions++;\r
-               TestEdge (start, dist, p1, j, k+1);\r
-               TestEdge (dist, end, j, p2, k+1);\r
-               return;\r
-       }\r
-\r
-       // the edge p1 to p2 is now free of tjunctions\r
-       if (numsuperverts >= MAX_SUPERVERTS)\r
-               Error ("MAX_SUPERVERTS");\r
-       superverts[numsuperverts] = p1;\r
-       numsuperverts++;\r
-}\r
-\r
-/*\r
-==================\r
-FixFaceEdges\r
-\r
-==================\r
-*/\r
-void FixFaceEdges (node_t *node, face_t *f)\r
-{\r
-       int             p1, p2;\r
-       int             i;\r
-       vec3_t  e2;\r
-       vec_t   len;\r
-       int             count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];\r
-       int             base;\r
-\r
-       if (f->merged || f->split[0] || f->split[1])\r
-               return;\r
-\r
-       numsuperverts = 0;\r
-\r
-       for (i=0 ; i<f->numpoints ; i++)\r
-       {\r
-               p1 = f->vertexnums[i];\r
-               p2 = f->vertexnums[(i+1)%f->numpoints];\r
-\r
-               VectorCopy (dvertexes[p1].point, edge_start);\r
-               VectorCopy (dvertexes[p2].point, e2);\r
-\r
-               FindEdgeVerts (edge_start, e2);\r
-\r
-               VectorSubtract (e2, edge_start, edge_dir);\r
-               len = VectorNormalize (edge_dir, edge_dir);\r
-\r
-               start[i] = numsuperverts;\r
-               TestEdge (0, len, p1, p2, 0);\r
-\r
-               count[i] = numsuperverts - start[i];\r
-       }\r
-\r
-       if (numsuperverts < 3)\r
-       {       // entire face collapsed\r
-               f->numpoints = 0;\r
-               c_facecollapse++;\r
-               return;\r
-       }\r
-\r
-       // we want to pick a vertex that doesn't have tjunctions\r
-       // on either side, which can cause artifacts on trifans,\r
-       // especially underwater\r
-       for (i=0 ; i<f->numpoints ; i++)\r
-       {\r
-               if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)\r
-                       break;\r
-       }\r
-       if (i == f->numpoints)\r
-       {\r
-               f->badstartvert = true;\r
-               c_badstartverts++;\r
-               base = 0;\r
-       }\r
-       else\r
-       {       // rotate the vertex order\r
-               base = start[i];\r
-       }\r
-\r
-       // this may fragment the face if > MAXEDGES\r
-       FaceFromSuperverts (node, f, base);\r
-}\r
-\r
-/*\r
-==================\r
-FixEdges_r\r
-==================\r
-*/\r
-void FixEdges_r (node_t *node)\r
-{\r
-       int             i;\r
-       face_t  *f;\r
-\r
-       if (node->planenum == PLANENUM_LEAF)\r
-               return;\r
-\r
-       for (f=node->faces ; f ; f=f->next)\r
-               FixFaceEdges (node, f);\r
-\r
-       for (i=0 ; i<2 ; i++)\r
-               FixEdges_r (node->children[i]);\r
-}\r
-\r
-/*\r
-===========\r
-FixTjuncs\r
-\r
-===========\r
-*/\r
-void FixTjuncs (node_t *headnode)\r
-{\r
-       // snap and merge all vertexes\r
-       Sys_FPrintf( SYS_VRB, "---- snap verts ----\n");\r
-       memset (hashverts, 0, sizeof(hashverts));\r
-       c_totalverts = 0;\r
-       c_uniqueverts = 0;\r
-       c_faceoverflows = 0;\r
-       EmitVertexes_r (headnode);\r
-       Sys_FPrintf( SYS_VRB, "%i unique from %i\n", c_uniqueverts, c_totalverts);\r
-\r
-       // break edges on tjunctions\r
-       Sys_FPrintf( SYS_VRB, "---- tjunc ----\n");\r
-       c_tryedges = 0;\r
-       c_degenerate = 0;\r
-       c_facecollapse = 0;\r
-       c_tjunctions = 0;\r
-       if (!notjunc)\r
-               FixEdges_r (headnode);\r
-       Sys_FPrintf( SYS_VRB, "%5i edges degenerated\n", c_degenerate);\r
-       Sys_FPrintf( SYS_VRB, "%5i faces degenerated\n", c_facecollapse);\r
-       Sys_FPrintf( SYS_VRB, "%5i edges added by tjunctions\n", c_tjunctions);\r
-       Sys_FPrintf( SYS_VRB, "%5i faces added by tjunctions\n", c_faceoverflows);\r
-       Sys_FPrintf( SYS_VRB, "%5i bad start verts\n", c_badstartverts);\r
-}\r
-\r
-\r
-//========================================================\r
-\r
-int            c_faces;\r
-\r
-face_t *AllocFace (void)\r
-{\r
-       face_t  *f;\r
-\r
-       f = malloc(sizeof(*f));\r
-       memset (f, 0, sizeof(*f));\r
-       c_faces++;\r
-\r
-       return f;\r
-}\r
-\r
-face_t *NewFaceFromFace (face_t *f)\r
-{\r
-       face_t  *newf;\r
-\r
-       newf = AllocFace ();\r
-       *newf = *f;\r
-       newf->merged = NULL;\r
-       newf->split[0] = newf->split[1] = NULL;\r
-       newf->w = NULL;\r
-       return newf;\r
-}\r
-\r
-void FreeFace (face_t *f)\r
-{\r
-       if (f->w)\r
-               FreeWinding (f->w);\r
-       free (f);\r
-       c_faces--;\r
-}\r
-\r
-//========================================================\r
-\r
-/*\r
-==================\r
-GetEdge\r
-\r
-Called by writebsp.\r
-Don't allow four way edges\r
-==================\r
-*/\r
-int GetEdge2 (int v1, int v2,  face_t *f)\r
-{\r
-       dedge_t *edge;\r
-       int             i;\r
-\r
-       c_tryedges++;\r
-\r
-       if (!noshare)\r
-       {\r
-               for (i=firstmodeledge ; i < numedges ; i++)\r
-               {\r
-                       edge = &dedges[i];\r
-                       if (v1 == edge->v[1] && v2 == edge->v[0]\r
-                       && edgefaces[i][0]->contents == f->contents)\r
-                       {\r
-                               if (edgefaces[i][1])\r
-       //                              Sys_Printf ("WARNING: multiple backward edge\n");\r
-                                       continue;\r
-                               edgefaces[i][1] = f;\r
-                               return -i;\r
-                       }\r
-       #if 0\r
-                       if (v1 == edge->v[0] && v2 == edge->v[1])\r
-                       {\r
-                               Sys_Printf ("WARNING: multiple forward edge\n");\r
-                               return i;\r
-                       }\r
-       #endif\r
-               }\r
-       }\r
-\r
-// emit an edge\r
-       if (numedges >= MAX_MAP_EDGES)\r
-               Error ("numedges == MAX_MAP_EDGES");\r
-       edge = &dedges[numedges];\r
-       numedges++;\r
-       edge->v[0] = v1;\r
-       edge->v[1] = v2;\r
-       edgefaces[numedges-1][0] = f;\r
-       \r
-       return numedges-1;\r
-}\r
-\r
-/*\r
-===========================================================================\r
-\r
-FACE MERGING\r
-\r
-===========================================================================\r
-*/\r
-\r
-#define        CONTINUOUS_EPSILON      0.001\r
-\r
-/*\r
-=============\r
-TryMergeWinding\r
-\r
-If two polygons share a common edge and the edges that meet at the\r
-common points are both inside the other polygons, merge them\r
-\r
-Returns NULL if the faces couldn't be merged, or the new face.\r
-The originals will NOT be freed.\r
-=============\r
-*/\r
-winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)\r
-{\r
-       vec_t           *p1, *p2, *p3, *p4, *back;\r
-       winding_t       *newf;\r
-       int                     i, j, k, l;\r
-       vec3_t          normal, delta;\r
-       vec_t           dot;\r
-       qboolean        keep1, keep2;\r
-       \r
-\r
-       //\r
-       // find a common edge\r
-       //      \r
-       p1 = p2 = NULL; // stop compiler warning\r
-       j = 0;                  // \r
-       \r
-       for (i=0 ; i<f1->numpoints ; i++)\r
-       {\r
-               p1 = f1->p[i];\r
-               p2 = f1->p[(i+1)%f1->numpoints];\r
-               for (j=0 ; j<f2->numpoints ; j++)\r
-               {\r
-                       p3 = f2->p[j];\r
-                       p4 = f2->p[(j+1)%f2->numpoints];\r
-                       for (k=0 ; k<3 ; k++)\r
-                       {\r
-                               if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON)\r
-                                       break;\r
-                               if (fabs(p2[k] - p3[k]) > EQUAL_EPSILON)\r
-                                       break;\r
-                       }\r
-                       if (k==3)\r
-                               break;\r
-               }\r
-               if (j < f2->numpoints)\r
-                       break;\r
-       }\r
-       \r
-       if (i == f1->numpoints)\r
-               return NULL;                    // no matching edges\r
-\r
-       //\r
-       // check slope of connected lines\r
-       // if the slopes are colinear, the point can be removed\r
-       //\r
-       back = f1->p[(i+f1->numpoints-1)%f1->numpoints];\r
-       VectorSubtract (p1, back, delta);\r
-       CrossProduct (planenormal, delta, normal);\r
-       VectorNormalize (normal, normal);\r
-       \r
-       back = f2->p[(j+2)%f2->numpoints];\r
-       VectorSubtract (back, p1, delta);\r
-       dot = DotProduct (delta, normal);\r
-       if (dot > CONTINUOUS_EPSILON)\r
-               return NULL;                    // not a convex polygon\r
-       keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);\r
-       \r
-       back = f1->p[(i+2)%f1->numpoints];\r
-       VectorSubtract (back, p2, delta);\r
-       CrossProduct (planenormal, delta, normal);\r
-       VectorNormalize (normal, normal);\r
-\r
-       back = f2->p[(j+f2->numpoints-1)%f2->numpoints];\r
-       VectorSubtract (back, p2, delta);\r
-       dot = DotProduct (delta, normal);\r
-       if (dot > CONTINUOUS_EPSILON)\r
-               return NULL;                    // not a convex polygon\r
-       keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);\r
-\r
-       //\r
-       // build the new polygon\r
-       //\r
-       newf = AllocWinding (f1->numpoints + f2->numpoints);\r
-       \r
-       // copy first polygon\r
-       for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)\r
-       {\r
-               if (k==(i+1)%f1->numpoints && !keep2)\r
-                       continue;\r
-               \r
-               VectorCopy (f1->p[k], newf->p[newf->numpoints]);\r
-               newf->numpoints++;\r
-       }\r
-       \r
-       // copy second polygon\r
-       for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)\r
-       {\r
-               if (l==(j+1)%f2->numpoints && !keep1)\r
-                       continue;\r
-               VectorCopy (f2->p[l], newf->p[newf->numpoints]);\r
-               newf->numpoints++;\r
-       }\r
-\r
-       return newf;\r
-}\r
-\r
-/*\r
-=============\r
-TryMerge\r
-\r
-If two polygons share a common edge and the edges that meet at the\r
-common points are both inside the other polygons, merge them\r
-\r
-Returns NULL if the faces couldn't be merged, or the new face.\r
-The originals will NOT be freed.\r
-=============\r
-*/\r
-face_t *TryMerge (face_t *f1, face_t *f2, vec3_t planenormal)\r
-{\r
-       face_t          *newf;\r
-       winding_t       *nw;\r
-\r
-       if (!f1->w || !f2->w)\r
-               return NULL;\r
-       if (f1->texinfo != f2->texinfo)\r
-               return NULL;\r
-       if (f1->planenum != f2->planenum)       // on front and back sides\r
-               return NULL;\r
-       if (f1->contents != f2->contents)\r
-               return NULL;\r
-               \r
-\r
-       nw = TryMergeWinding (f1->w, f2->w, planenormal);\r
-       if (!nw)\r
-               return NULL;\r
-\r
-       c_merge++;\r
-       newf = NewFaceFromFace (f1);\r
-       newf->w = nw;\r
-\r
-       f1->merged = newf;\r
-       f2->merged = newf;\r
-\r
-       return newf;\r
-}\r
-\r
-/*\r
-===============\r
-MergeNodeFaces\r
-===============\r
-*/\r
-void MergeNodeFaces (node_t *node)\r
-{\r
-       face_t  *f1, *f2, *end;\r
-       face_t  *merged;\r
-       plane_t *plane;\r
-\r
-       plane = &mapplanes[node->planenum];\r
-       merged = NULL;\r
-       \r
-       for (f1 = node->faces ; f1 ; f1 = f1->next)\r
-       {\r
-               if (f1->merged || f1->split[0] || f1->split[1])\r
-                       continue;\r
-               for (f2 = node->faces ; f2 != f1 ; f2=f2->next)\r
-               {\r
-                       if (f2->merged || f2->split[0] || f2->split[1])\r
-                               continue;\r
-                       merged = TryMerge (f1, f2, plane->normal);\r
-                       if (!merged)\r
-                               continue;\r
-\r
-                       // add merged to the end of the node face list \r
-                       // so it will be checked against all the faces again\r
-                       for (end = node->faces ; end->next ; end = end->next)\r
-                       ;\r
-                       merged->next = NULL;\r
-                       end->next = merged;\r
-                       break;\r
-               }\r
-       }\r
-}\r
-\r
-//=====================================================================\r
-\r
-/*\r
-===============\r
-SubdivideFace\r
-\r
-Chop up faces that are larger than we want in the surface cache\r
-===============\r
-*/\r
-void SubdivideFace (node_t *node, face_t *f)\r
-{\r
-       float           mins, maxs;\r
-       vec_t           v;\r
-       int                     axis, i;\r
-       texinfo_t       *tex;\r
-       vec3_t          temp;\r
-       vec_t           dist;\r
-       winding_t       *w, *frontw, *backw;\r
-\r
-       if (f->merged)\r
-               return;\r
-\r
-// special (non-surface cached) faces don't need subdivision\r
-       tex = &texinfo[f->texinfo];\r
-\r
-       if ( tex->flags & (SURF_WARP|SURF_SKY) )\r
-       {\r
-               return;\r
-       }\r
-\r
-       for (axis = 0 ; axis < 2 ; axis++)\r
-       {\r
-               while (1)\r
-               {\r
-                       mins = 999999;\r
-                       maxs = -999999;\r
-                       \r
-                       VectorCopy (tex->vecs[axis], temp);\r
-                       w = f->w;\r
-                       for (i=0 ; i<w->numpoints ; i++)\r
-                       {\r
-                               v = DotProduct (w->p[i], temp);\r
-                               if (v < mins)\r
-                                       mins = v;\r
-                               if (v > maxs)\r
-                                       maxs = v;\r
-                       }\r
-#if 0\r
-                       if (maxs - mins <= 0)\r
-                               Error ("zero extents");\r
-#endif\r
-                       if (axis == 2)\r
-                       {       // allow double high walls\r
-                               if (maxs - mins <= subdivide_size/* *2 */)\r
-                                       break;\r
-                       }\r
-                       else if (maxs - mins <= subdivide_size)\r
-                               break;\r
-                       \r
-               // split it\r
-                       c_subdivide++;\r
-                       \r
-                       v = VectorNormalize (temp, temp);       \r
-\r
-                       dist = (mins + subdivide_size - 16)/v;\r
-\r
-                       ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);\r
-                       if (!frontw || !backw)\r
-                               Error ("SubdivideFace: didn't split the polygon");\r
-\r
-                       f->split[0] = NewFaceFromFace (f);\r
-                       f->split[0]->w = frontw;\r
-                       f->split[0]->next = node->faces;\r
-                       node->faces = f->split[0];\r
-\r
-                       f->split[1] = NewFaceFromFace (f);\r
-                       f->split[1]->w = backw;\r
-                       f->split[1]->next = node->faces;\r
-                       node->faces = f->split[1];\r
-\r
-                       SubdivideFace (node, f->split[0]);\r
-                       SubdivideFace (node, f->split[1]);\r
-                       return;\r
-               }\r
-       }\r
-}\r
-\r
-void SubdivideNodeFaces (node_t *node)\r
-{\r
-       face_t  *f;\r
-\r
-       for (f = node->faces ; f ; f=f->next)\r
-       {\r
-               SubdivideFace (node, f);\r
-       }\r
-}\r
-\r
-//===========================================================================\r
-\r
-int    c_nodefaces;\r
-\r
-\r
-/*\r
-============\r
-FaceFromPortal\r
-\r
-============\r
-*/\r
-face_t *FaceFromPortal (portal_t *p, int pside)\r
-{\r
-       face_t  *f;\r
-       side_t  *side;\r
-\r
-       side = p->side;\r
-       if (!side)\r
-               return NULL;    // portal does not bridge different visible contents\r
-\r
-       f = AllocFace ();\r
-\r
-       f->texinfo = side->texinfo;\r
-       f->planenum = (side->planenum & ~1) | pside;\r
-       f->portal = p;\r
-\r
-       if ( (p->nodes[pside]->contents & CONTENTS_WINDOW)\r
-               && VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents) == CONTENTS_WINDOW )\r
-               return NULL;    // don't show insides of windows\r
-\r
-       if (pside)\r
-       {\r
-               f->w = ReverseWinding(p->winding);\r
-               f->contents = p->nodes[1]->contents;\r
-       }\r
-       else\r
-       {\r
-               f->w = CopyWinding(p->winding);\r
-               f->contents = p->nodes[0]->contents;\r
-       }\r
-       return f;\r
-}\r
-\r
-\r
-/*\r
-===============\r
-MakeFaces_r\r
-\r
-If a portal will make a visible face,\r
-mark the side that originally created it\r
-\r
-  solid / empty : solid\r
-  solid / water : solid\r
-  water / empty : water\r
-  water / water : none\r
-===============\r
-*/\r
-void MakeFaces_r (node_t *node)\r
-{\r
-       portal_t        *p;\r
-       int                     s;\r
-\r
-       // recurse down to leafs\r
-       if (node->planenum != PLANENUM_LEAF)\r
-       {\r
-               MakeFaces_r (node->children[0]);\r
-               MakeFaces_r (node->children[1]);\r
-\r
-               // merge together all visible faces on the node\r
-               if (!nomerge)\r
-                       MergeNodeFaces (node);\r
-               if (!nosubdiv)\r
-                       SubdivideNodeFaces (node);\r
-\r
-               return;\r
-       }\r
-\r
-       // solid leafs never have visible faces\r
-       if (node->contents & CONTENTS_SOLID)\r
-               return;\r
-\r
-       // see which portals are valid\r
-       for (p=node->portals ; p ; p = p->next[s])\r
-       {\r
-               s = (p->nodes[1] == node);\r
-\r
-               p->face[s] = FaceFromPortal (p, s);\r
-               if (p->face[s])\r
-               {\r
-                       c_nodefaces++;\r
-                       p->face[s]->next = p->onnode->faces;\r
-                       p->onnode->faces = p->face[s];\r
-               }\r
-       }\r
-}\r
-\r
-/*\r
-============\r
-MakeFaces\r
-============\r
-*/\r
-void MakeFaces (node_t *node)\r
-{\r
-       Sys_FPrintf( SYS_VRB, "--- MakeFaces ---\n");\r
-       c_merge = 0;\r
-       c_subdivide = 0;\r
-       c_nodefaces = 0;\r
-\r
-       MakeFaces_r (node);\r
-\r
-       Sys_FPrintf( SYS_VRB, "%5i makefaces\n", c_nodefaces);\r
-       Sys_FPrintf( SYS_VRB, "%5i merged\n", c_merge);\r
-       Sys_FPrintf( SYS_VRB, "%5i subdivided\n", c_subdivide);\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
+*/
+// faces.c
+
+#include "qbsp.h"
+
+/*
+
+  some faces will be removed before saving, but still form nodes:
+
+  the insides of sky volumes
+  meeting planes of different water current volumes
+
+*/
+
+// undefine for dumb linear searches
+#define        USE_HASHING
+
+#define        INTEGRAL_EPSILON        0.01
+#define        POINT_EPSILON           0.5
+#define        OFF_EPSILON                     0.5
+
+int    c_merge;
+int    c_subdivide;
+
+int    c_totalverts;
+int    c_uniqueverts;
+int    c_degenerate;
+int    c_tjunctions;
+int    c_faceoverflows;
+int    c_facecollapse;
+int    c_badstartverts;
+
+#define        MAX_SUPERVERTS  512
+int    superverts[MAX_SUPERVERTS];
+int    numsuperverts;
+
+face_t         *edgefaces[MAX_MAP_EDGES][2];
+int            firstmodeledge = 1;
+int            firstmodelface;
+
+int    c_tryedges;
+
+vec3_t edge_dir;
+vec3_t edge_start;
+vec_t  edge_len;
+
+int            num_edge_verts;
+int            edge_verts[MAX_MAP_VERTS];
+
+
+float  subdivide_size = 240;
+
+
+face_t *NewFaceFromFace (face_t *f);
+
+//===========================================================================
+
+typedef struct hashvert_s
+{
+       struct hashvert_s       *next;
+       int             num;
+} hashvert_t;
+
+
+#define        HASH_SIZE       64
+
+
+int    vertexchain[MAX_MAP_VERTS];             // the next vertex in a hash chain
+int    hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts
+
+face_t         *edgefaces[MAX_MAP_EDGES][2];
+
+//============================================================================
+
+
+unsigned HashVec (vec3_t vec)
+{
+       int                     x, y;
+
+       x = (4096 + (int)(vec[0]+0.5)) >> 7;
+       y = (4096 + (int)(vec[1]+0.5)) >> 7;
+
+       if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )
+               Error ("HashVec: point outside valid range");
+       
+       return y*HASH_SIZE + x;
+}
+
+#ifdef USE_HASHING
+/*
+=============
+GetVertex
+
+Uses hashing
+=============
+*/
+int    GetVertexnum (vec3_t in)
+{
+       int                     h;
+       int                     i;
+       float           *p;
+       vec3_t          vert;
+       int                     vnum;
+
+       c_totalverts++;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if ( fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON)
+                       vert[i] = Q_rint(in[i]);
+               else
+                       vert[i] = in[i];
+       }
+       
+       h = HashVec (vert);
+       
+       for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])
+       {
+               p = dvertexes[vnum].point;
+               if ( fabs(p[0]-vert[0])<POINT_EPSILON
+               && fabs(p[1]-vert[1])<POINT_EPSILON
+               && fabs(p[2]-vert[2])<POINT_EPSILON )
+                       return vnum;
+       }
+       
+// emit a vertex
+       if (numvertexes == MAX_MAP_VERTS)
+               Error ("numvertexes == MAX_MAP_VERTS");
+
+       dvertexes[numvertexes].point[0] = vert[0];
+       dvertexes[numvertexes].point[1] = vert[1];
+       dvertexes[numvertexes].point[2] = vert[2];
+
+       vertexchain[numvertexes] = hashverts[h];
+       hashverts[h] = numvertexes;
+
+       c_uniqueverts++;
+
+       numvertexes++;
+               
+       return numvertexes-1;
+}
+#else
+/*
+==================
+GetVertexnum
+
+Dumb linear search
+==================
+*/
+int    GetVertexnum (vec3_t v)
+{
+       int                     i, j;
+       dvertex_t       *dv;
+       vec_t           d;
+
+       c_totalverts++;
+
+       // make really close values exactly integral
+       for (i=0 ; i<3 ; i++)
+       {
+               if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )
+                       v[i] = (int)(v[i]+0.5);
+               if (v[i] < -4096 || v[i] > 4096)
+                       Error ("GetVertexnum: outside +/- 4096");
+       }
+
+       // search for an existing vertex match
+       for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       d = v[j] - dv->point[j];
+                       if ( d > POINT_EPSILON || d < -POINT_EPSILON)
+                               break;
+               }
+               if (j == 3)
+                       return i;               // a match
+       }
+
+       // new point
+       if (numvertexes == MAX_MAP_VERTS)
+               Error ("MAX_MAP_VERTS");
+       VectorCopy (v, dv->point);
+       numvertexes++;
+       c_uniqueverts++;
+
+       return numvertexes-1;
+}
+#endif
+
+
+/*
+==================
+FaceFromSuperverts
+
+The faces vertexes have beeb added to the superverts[] array,
+and there may be more there than can be held in a face (MAXEDGES).
+
+If less, the faces vertexnums[] will be filled in, otherwise
+face will reference a tree of split[] faces until all of the
+vertexnums can be added.
+
+superverts[base] will become face->vertexnums[0], and the others
+will be circularly filled in.
+==================
+*/
+void FaceFromSuperverts (node_t *node, face_t *f, int base)
+{
+       face_t  *newf;
+       int             remaining;
+       int             i;
+
+       remaining = numsuperverts;
+       while (remaining > MAXEDGES)
+       {       // must split into two faces, because of vertex overload
+               c_faceoverflows++;
+
+               newf = f->split[0] = NewFaceFromFace (f);
+               newf = f->split[0];
+               newf->next = node->faces;
+               node->faces = newf;
+
+               newf->numpoints = MAXEDGES;
+               for (i=0 ; i<MAXEDGES ; i++)
+                       newf->vertexnums[i] = superverts[(i+base)%numsuperverts];
+
+               f->split[1] = NewFaceFromFace (f);
+               f = f->split[1];
+               f->next = node->faces;
+               node->faces = f;
+
+               remaining -= (MAXEDGES-2);
+               base = (base+MAXEDGES-1)%numsuperverts;
+       }
+
+       // copy the vertexes back to the face
+       f->numpoints = remaining;
+       for (i=0 ; i<remaining ; i++)
+               f->vertexnums[i] = superverts[(i+base)%numsuperverts];
+}
+
+
+/*
+==================
+EmitFaceVertexes
+==================
+*/
+void EmitFaceVertexes (node_t *node, face_t *f)
+{
+       winding_t       *w;
+       int                     i;
+
+       if (f->merged || f->split[0] || f->split[1])
+               return;
+
+       w = f->w;
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               if (noweld)
+               {       // make every point unique
+                       if (numvertexes == MAX_MAP_VERTS)
+                               Error ("MAX_MAP_VERTS");
+                       superverts[i] = numvertexes;
+                       VectorCopy (w->p[i], dvertexes[numvertexes].point);
+                       numvertexes++;
+                       c_uniqueverts++;
+                       c_totalverts++;
+               }
+               else
+                       superverts[i] = GetVertexnum (w->p[i]);
+       }
+       numsuperverts = w->numpoints;
+
+       // this may fragment the face if > MAXEDGES
+       FaceFromSuperverts (node, f, 0);
+}
+
+/*
+==================
+EmitVertexes_r
+==================
+*/
+void EmitVertexes_r (node_t *node)
+{
+       int             i;
+       face_t  *f;
+
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       for (f=node->faces ; f ; f=f->next)
+       {
+               EmitFaceVertexes (node, f);
+       }
+
+       for (i=0 ; i<2 ; i++)
+               EmitVertexes_r (node->children[i]);
+}
+
+
+#ifdef USE_HASHING
+/*
+==========
+FindEdgeVerts
+
+Uses the hash tables to cut down to a small number
+==========
+*/
+void FindEdgeVerts (vec3_t v1, vec3_t v2)
+{
+       int             x1, x2, y1, y2, t;
+       int             x, y;
+       int             vnum;
+
+#if 0
+{
+       int             i;
+       num_edge_verts = numvertexes-1;
+       for (i=0 ; i<numvertexes-1 ; i++)
+               edge_verts[i] = i+1;
+}
+#endif
+
+       x1 = (4096 + (int)(v1[0]+0.5)) >> 7;
+       y1 = (4096 + (int)(v1[1]+0.5)) >> 7;
+       x2 = (4096 + (int)(v2[0]+0.5)) >> 7;
+       y2 = (4096 + (int)(v2[1]+0.5)) >> 7;
+
+       if (x1 > x2)
+       {
+               t = x1;
+               x1 = x2;
+               x2 = t;
+       }
+       if (y1 > y2)
+       {
+               t = y1;
+               y1 = y2;
+               y2 = t;
+       }
+#if 0
+       x1--;
+       x2++;
+       y1--;
+       y2++;
+       if (x1 < 0)
+               x1 = 0;
+       if (x2 >= HASH_SIZE)
+               x2 = HASH_SIZE;
+       if (y1 < 0)
+               y1 = 0;
+       if (y2 >= HASH_SIZE)
+               y2 = HASH_SIZE;
+#endif
+       num_edge_verts = 0;
+       for (x=x1 ; x <= x2 ; x++)
+       {
+               for (y=y1 ; y <= y2 ; y++)
+               {
+                       for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])
+                       {
+                               edge_verts[num_edge_verts++] = vnum;
+                       }
+               }
+       }
+}
+
+#else
+/*
+==========
+FindEdgeVerts
+
+Forced a dumb check of everything
+==========
+*/
+void FindEdgeVerts (vec3_t v1, vec3_t v2)
+{
+       int             i;
+
+       num_edge_verts = numvertexes-1;
+       for (i=0 ; i<num_edge_verts ; i++)
+               edge_verts[i] = i+1;
+}
+#endif
+
+/*
+==========
+TestEdge
+
+Can be recursively reentered
+==========
+*/
+void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)
+{
+       int             j, k;
+       vec_t   dist;
+       vec3_t  delta;
+       vec3_t  exact;
+       vec3_t  off;
+       vec_t   error;
+       vec3_t  p;
+
+       if (p1 == p2)
+       {
+               c_degenerate++;
+               return;         // degenerate edge
+       }
+
+       for (k=startvert ; k<num_edge_verts ; k++)
+       {
+               j = edge_verts[k];
+               if (j==p1 || j == p2)
+                       continue;
+
+               VectorCopy (dvertexes[j].point, p);
+
+               VectorSubtract (p, edge_start, delta);
+               dist = DotProduct (delta, edge_dir);
+               if (dist <=start || dist >= end)
+                       continue;               // off an end
+               VectorMA (edge_start, dist, edge_dir, exact);
+               VectorSubtract (p, exact, off);
+               error = VectorLength (off);
+
+               if (fabs(error) > OFF_EPSILON)
+                       continue;               // not on the edge
+
+               // break the edge
+               c_tjunctions++;
+               TestEdge (start, dist, p1, j, k+1);
+               TestEdge (dist, end, j, p2, k+1);
+               return;
+       }
+
+       // the edge p1 to p2 is now free of tjunctions
+       if (numsuperverts >= MAX_SUPERVERTS)
+               Error ("MAX_SUPERVERTS");
+       superverts[numsuperverts] = p1;
+       numsuperverts++;
+}
+
+/*
+==================
+FixFaceEdges
+
+==================
+*/
+void FixFaceEdges (node_t *node, face_t *f)
+{
+       int             p1, p2;
+       int             i;
+       vec3_t  e2;
+       vec_t   len;
+       int             count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
+       int             base;
+
+       if (f->merged || f->split[0] || f->split[1])
+               return;
+
+       numsuperverts = 0;
+
+       for (i=0 ; i<f->numpoints ; i++)
+       {
+               p1 = f->vertexnums[i];
+               p2 = f->vertexnums[(i+1)%f->numpoints];
+
+               VectorCopy (dvertexes[p1].point, edge_start);
+               VectorCopy (dvertexes[p2].point, e2);
+
+               FindEdgeVerts (edge_start, e2);
+
+               VectorSubtract (e2, edge_start, edge_dir);
+               len = VectorNormalize (edge_dir, edge_dir);
+
+               start[i] = numsuperverts;
+               TestEdge (0, len, p1, p2, 0);
+
+               count[i] = numsuperverts - start[i];
+       }
+
+       if (numsuperverts < 3)
+       {       // entire face collapsed
+               f->numpoints = 0;
+               c_facecollapse++;
+               return;
+       }
+
+       // we want to pick a vertex that doesn't have tjunctions
+       // on either side, which can cause artifacts on trifans,
+       // especially underwater
+       for (i=0 ; i<f->numpoints ; i++)
+       {
+               if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)
+                       break;
+       }
+       if (i == f->numpoints)
+       {
+               f->badstartvert = true;
+               c_badstartverts++;
+               base = 0;
+       }
+       else
+       {       // rotate the vertex order
+               base = start[i];
+       }
+
+       // this may fragment the face if > MAXEDGES
+       FaceFromSuperverts (node, f, base);
+}
+
+/*
+==================
+FixEdges_r
+==================
+*/
+void FixEdges_r (node_t *node)
+{
+       int             i;
+       face_t  *f;
+
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       for (f=node->faces ; f ; f=f->next)
+               FixFaceEdges (node, f);
+
+       for (i=0 ; i<2 ; i++)
+               FixEdges_r (node->children[i]);
+}
+
+/*
+===========
+FixTjuncs
+
+===========
+*/
+void FixTjuncs (node_t *headnode)
+{
+       // snap and merge all vertexes
+       Sys_FPrintf( SYS_VRB, "---- snap verts ----\n");
+       memset (hashverts, 0, sizeof(hashverts));
+       c_totalverts = 0;
+       c_uniqueverts = 0;
+       c_faceoverflows = 0;
+       EmitVertexes_r (headnode);
+       Sys_FPrintf( SYS_VRB, "%i unique from %i\n", c_uniqueverts, c_totalverts);
+
+       // break edges on tjunctions
+       Sys_FPrintf( SYS_VRB, "---- tjunc ----\n");
+       c_tryedges = 0;
+       c_degenerate = 0;
+       c_facecollapse = 0;
+       c_tjunctions = 0;
+       if (!notjunc)
+               FixEdges_r (headnode);
+       Sys_FPrintf( SYS_VRB, "%5i edges degenerated\n", c_degenerate);
+       Sys_FPrintf( SYS_VRB, "%5i faces degenerated\n", c_facecollapse);
+       Sys_FPrintf( SYS_VRB, "%5i edges added by tjunctions\n", c_tjunctions);
+       Sys_FPrintf( SYS_VRB, "%5i faces added by tjunctions\n", c_faceoverflows);
+       Sys_FPrintf( SYS_VRB, "%5i bad start verts\n", c_badstartverts);
+}
+
+
+//========================================================
+
+int            c_faces;
+
+face_t *AllocFace (void)
+{
+       face_t  *f;
+
+       f = malloc(sizeof(*f));
+       memset (f, 0, sizeof(*f));
+       c_faces++;
+
+       return f;
+}
+
+face_t *NewFaceFromFace (face_t *f)
+{
+       face_t  *newf;
+
+       newf = AllocFace ();
+       *newf = *f;
+       newf->merged = NULL;
+       newf->split[0] = newf->split[1] = NULL;
+       newf->w = NULL;
+       return newf;
+}
+
+void FreeFace (face_t *f)
+{
+       if (f->w)
+               FreeWinding (f->w);
+       free (f);
+       c_faces--;
+}
+
+//========================================================
+
+/*
+==================
+GetEdge
+
+Called by writebsp.
+Don't allow four way edges
+==================
+*/
+int GetEdge2 (int v1, int v2,  face_t *f)
+{
+       dedge_t *edge;
+       int             i;
+
+       c_tryedges++;
+
+       if (!noshare)
+       {
+               for (i=firstmodeledge ; i < numedges ; i++)
+               {
+                       edge = &dedges[i];
+                       if (v1 == edge->v[1] && v2 == edge->v[0]
+                       && edgefaces[i][0]->contents == f->contents)
+                       {
+                               if (edgefaces[i][1])
+       //                              Sys_Printf ("WARNING: multiple backward edge\n");
+                                       continue;
+                               edgefaces[i][1] = f;
+                               return -i;
+                       }
+       #if 0
+                       if (v1 == edge->v[0] && v2 == edge->v[1])
+                       {
+                               Sys_Printf ("WARNING: multiple forward edge\n");
+                               return i;
+                       }
+       #endif
+               }
+       }
+
+// emit an edge
+       if (numedges >= MAX_MAP_EDGES)
+               Error ("numedges == MAX_MAP_EDGES");
+       edge = &dedges[numedges];
+       numedges++;
+       edge->v[0] = v1;
+       edge->v[1] = v2;
+       edgefaces[numedges-1][0] = f;
+       
+       return numedges-1;
+}
+
+/*
+===========================================================================
+
+FACE MERGING
+
+===========================================================================
+*/
+
+#define        CONTINUOUS_EPSILON      0.001
+
+/*
+=============
+TryMergeWinding
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)
+{
+       vec_t           *p1, *p2, *p3, *p4, *back;
+       winding_t       *newf;
+       int                     i, j, k, l;
+       vec3_t          normal, delta;
+       vec_t           dot;
+       qboolean        keep1, keep2;
+       
+
+       //
+       // find a common edge
+       //      
+       p1 = p2 = NULL; // stop compiler warning
+       j = 0;                  // 
+       
+       for (i=0 ; i<f1->numpoints ; i++)
+       {
+               p1 = f1->p[i];
+               p2 = f1->p[(i+1)%f1->numpoints];
+               for (j=0 ; j<f2->numpoints ; j++)
+               {
+                       p3 = f2->p[j];
+                       p4 = f2->p[(j+1)%f2->numpoints];
+                       for (k=0 ; k<3 ; k++)
+                       {
+                               if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON)
+                                       break;
+                               if (fabs(p2[k] - p3[k]) > EQUAL_EPSILON)
+                                       break;
+                       }
+                       if (k==3)
+                               break;
+               }
+               if (j < f2->numpoints)
+                       break;
+       }
+       
+       if (i == f1->numpoints)
+               return NULL;                    // no matching edges
+
+       //
+       // check slope of connected lines
+       // if the slopes are colinear, the point can be removed
+       //
+       back = f1->p[(i+f1->numpoints-1)%f1->numpoints];
+       VectorSubtract (p1, back, delta);
+       CrossProduct (planenormal, delta, normal);
+       VectorNormalize (normal, normal);
+       
+       back = f2->p[(j+2)%f2->numpoints];
+       VectorSubtract (back, p1, delta);
+       dot = DotProduct (delta, normal);
+       if (dot > CONTINUOUS_EPSILON)
+               return NULL;                    // not a convex polygon
+       keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+       
+       back = f1->p[(i+2)%f1->numpoints];
+       VectorSubtract (back, p2, delta);
+       CrossProduct (planenormal, delta, normal);
+       VectorNormalize (normal, normal);
+
+       back = f2->p[(j+f2->numpoints-1)%f2->numpoints];
+       VectorSubtract (back, p2, delta);
+       dot = DotProduct (delta, normal);
+       if (dot > CONTINUOUS_EPSILON)
+               return NULL;                    // not a convex polygon
+       keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+       //
+       // build the new polygon
+       //
+       newf = AllocWinding (f1->numpoints + f2->numpoints);
+       
+       // copy first polygon
+       for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
+       {
+               if (k==(i+1)%f1->numpoints && !keep2)
+                       continue;
+               
+               VectorCopy (f1->p[k], newf->p[newf->numpoints]);
+               newf->numpoints++;
+       }
+       
+       // copy second polygon
+       for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
+       {
+               if (l==(j+1)%f2->numpoints && !keep1)
+                       continue;
+               VectorCopy (f2->p[l], newf->p[newf->numpoints]);
+               newf->numpoints++;
+       }
+
+       return newf;
+}
+
+/*
+=============
+TryMerge
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+face_t *TryMerge (face_t *f1, face_t *f2, vec3_t planenormal)
+{
+       face_t          *newf;
+       winding_t       *nw;
+
+       if (!f1->w || !f2->w)
+               return NULL;
+       if (f1->texinfo != f2->texinfo)
+               return NULL;
+       if (f1->planenum != f2->planenum)       // on front and back sides
+               return NULL;
+       if (f1->contents != f2->contents)
+               return NULL;
+               
+
+       nw = TryMergeWinding (f1->w, f2->w, planenormal);
+       if (!nw)
+               return NULL;
+
+       c_merge++;
+       newf = NewFaceFromFace (f1);
+       newf->w = nw;
+
+       f1->merged = newf;
+       f2->merged = newf;
+
+       return newf;
+}
+
+/*
+===============
+MergeNodeFaces
+===============
+*/
+void MergeNodeFaces (node_t *node)
+{
+       face_t  *f1, *f2, *end;
+       face_t  *merged;
+       plane_t *plane;
+
+       plane = &mapplanes[node->planenum];
+       merged = NULL;
+       
+       for (f1 = node->faces ; f1 ; f1 = f1->next)
+       {
+               if (f1->merged || f1->split[0] || f1->split[1])
+                       continue;
+               for (f2 = node->faces ; f2 != f1 ; f2=f2->next)
+               {
+                       if (f2->merged || f2->split[0] || f2->split[1])
+                               continue;
+                       merged = TryMerge (f1, f2, plane->normal);
+                       if (!merged)
+                               continue;
+
+                       // add merged to the end of the node face list 
+                       // so it will be checked against all the faces again
+                       for (end = node->faces ; end->next ; end = end->next)
+                       ;
+                       merged->next = NULL;
+                       end->next = merged;
+                       break;
+               }
+       }
+}
+
+//=====================================================================
+
+/*
+===============
+SubdivideFace
+
+Chop up faces that are larger than we want in the surface cache
+===============
+*/
+void SubdivideFace (node_t *node, face_t *f)
+{
+       float           mins, maxs;
+       vec_t           v;
+       int                     axis, i;
+       texinfo_t       *tex;
+       vec3_t          temp;
+       vec_t           dist;
+       winding_t       *w, *frontw, *backw;
+
+       if (f->merged)
+               return;
+
+// special (non-surface cached) faces don't need subdivision
+       tex = &texinfo[f->texinfo];
+
+       if ( tex->flags & (SURF_WARP|SURF_SKY) )
+       {
+               return;
+       }
+
+       for (axis = 0 ; axis < 2 ; axis++)
+       {
+               while (1)
+               {
+                       mins = 999999;
+                       maxs = -999999;
+                       
+                       VectorCopy (tex->vecs[axis], temp);
+                       w = f->w;
+                       for (i=0 ; i<w->numpoints ; i++)
+                       {
+                               v = DotProduct (w->p[i], temp);
+                               if (v < mins)
+                                       mins = v;
+                               if (v > maxs)
+                                       maxs = v;
+                       }
+#if 0
+                       if (maxs - mins <= 0)
+                               Error ("zero extents");
+#endif
+                       if (axis == 2)
+                       {       // allow double high walls
+                               if (maxs - mins <= subdivide_size/* *2 */)
+                                       break;
+                       }
+                       else if (maxs - mins <= subdivide_size)
+                               break;
+                       
+               // split it
+                       c_subdivide++;
+                       
+                       v = VectorNormalize (temp, temp);       
+
+                       dist = (mins + subdivide_size - 16)/v;
+
+                       ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);
+                       if (!frontw || !backw)
+                               Error ("SubdivideFace: didn't split the polygon");
+
+                       f->split[0] = NewFaceFromFace (f);
+                       f->split[0]->w = frontw;
+                       f->split[0]->next = node->faces;
+                       node->faces = f->split[0];
+
+                       f->split[1] = NewFaceFromFace (f);
+                       f->split[1]->w = backw;
+                       f->split[1]->next = node->faces;
+                       node->faces = f->split[1];
+
+                       SubdivideFace (node, f->split[0]);
+                       SubdivideFace (node, f->split[1]);
+                       return;
+               }
+       }
+}
+
+void SubdivideNodeFaces (node_t *node)
+{
+       face_t  *f;
+
+       for (f = node->faces ; f ; f=f->next)
+       {
+               SubdivideFace (node, f);
+       }
+}
+
+//===========================================================================
+
+int    c_nodefaces;
+
+
+/*
+============
+FaceFromPortal
+
+============
+*/
+face_t *FaceFromPortal (portal_t *p, int pside)
+{
+       face_t  *f;
+       side_t  *side;
+
+       side = p->side;
+       if (!side)
+               return NULL;    // portal does not bridge different visible contents
+
+       f = AllocFace ();
+
+       f->texinfo = side->texinfo;
+       f->planenum = (side->planenum & ~1) | pside;
+       f->portal = p;
+
+       if ( (p->nodes[pside]->contents & CONTENTS_WINDOW)
+               && VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents) == CONTENTS_WINDOW )
+               return NULL;    // don't show insides of windows
+
+       if (pside)
+       {
+               f->w = ReverseWinding(p->winding);
+               f->contents = p->nodes[1]->contents;
+       }
+       else
+       {
+               f->w = CopyWinding(p->winding);
+               f->contents = p->nodes[0]->contents;
+       }
+       return f;
+}
+
+
+/*
+===============
+MakeFaces_r
+
+If a portal will make a visible face,
+mark the side that originally created it
+
+  solid / empty : solid
+  solid / water : solid
+  water / empty : water
+  water / water : none
+===============
+*/
+void MakeFaces_r (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+
+       // recurse down to leafs
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               MakeFaces_r (node->children[0]);
+               MakeFaces_r (node->children[1]);
+
+               // merge together all visible faces on the node
+               if (!nomerge)
+                       MergeNodeFaces (node);
+               if (!nosubdiv)
+                       SubdivideNodeFaces (node);
+
+               return;
+       }
+
+       // solid leafs never have visible faces
+       if (node->contents & CONTENTS_SOLID)
+               return;
+
+       // see which portals are valid
+       for (p=node->portals ; p ; p = p->next[s])
+       {
+               s = (p->nodes[1] == node);
+
+               p->face[s] = FaceFromPortal (p, s);
+               if (p->face[s])
+               {
+                       c_nodefaces++;
+                       p->face[s]->next = p->onnode->faces;
+                       p->onnode->faces = p->face[s];
+               }
+       }
+}
+
+/*
+============
+MakeFaces
+============
+*/
+void MakeFaces (node_t *node)
+{
+       Sys_FPrintf( SYS_VRB, "--- MakeFaces ---\n");
+       c_merge = 0;
+       c_subdivide = 0;
+       c_nodefaces = 0;
+
+       MakeFaces_r (node);
+
+       Sys_FPrintf( SYS_VRB, "%5i makefaces\n", c_nodefaces);
+       Sys_FPrintf( SYS_VRB, "%5i merged\n", c_merge);
+       Sys_FPrintf( SYS_VRB, "%5i subdivided\n", c_subdivide);
+}