]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/q2map/portals.c
eol style
[xonotic/netradiant.git] / tools / quake2 / q2map / portals.c
index a65b66b2664940e1d146752f3f22f05466d03db9..9857a3bae4e567d43b9fa8a7ee18438b64370c18 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-*/\r
-\r
-#include "qbsp.h"\r
-\r
-\r
-int            c_active_portals;\r
-int            c_peak_portals;\r
-int            c_boundary;\r
-int            c_boundary_sides;\r
-\r
-/*\r
-===========\r
-AllocPortal\r
-===========\r
-*/\r
-portal_t *AllocPortal (void)\r
-{\r
-       portal_t        *p;\r
-       \r
-       if (numthreads == 1)\r
-               c_active_portals++;\r
-       if (c_active_portals > c_peak_portals)\r
-               c_peak_portals = c_active_portals;\r
-       \r
-       p = malloc (sizeof(portal_t));\r
-       memset (p, 0, sizeof(portal_t));\r
-       \r
-       return p;\r
-}\r
-\r
-void FreePortal (portal_t *p)\r
-{\r
-       if (p->winding)\r
-               FreeWinding (p->winding);\r
-       if (numthreads == 1)\r
-               c_active_portals--;\r
-       free (p);\r
-}\r
-\r
-//==============================================================\r
-\r
-/*\r
-==============\r
-VisibleContents\r
-\r
-Returns the single content bit of the\r
-strongest visible content present\r
-==============\r
-*/\r
-int VisibleContents (int contents)\r
-{\r
-       int             i;\r
-\r
-       for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)\r
-               if (contents & i )\r
-                       return i;\r
-\r
-       return 0;\r
-}\r
-\r
-\r
-/*\r
-===============\r
-ClusterContents\r
-===============\r
-*/\r
-int ClusterContents (node_t *node)\r
-{\r
-       int             c1, c2, c;\r
-\r
-       if (node->planenum == PLANENUM_LEAF)\r
-               return node->contents;\r
-\r
-       c1 = ClusterContents(node->children[0]);\r
-       c2 = ClusterContents(node->children[1]);\r
-       c = c1|c2;\r
-\r
-       // a cluster may include some solid detail areas, but\r
-       // still be seen into\r
-       if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )\r
-               c &= ~CONTENTS_SOLID;\r
-       return c;\r
-}\r
-\r
-/*\r
-=============\r
-Portal_VisFlood\r
-\r
-Returns true if the portal is empty or translucent, allowing\r
-the PVS calculation to see through it.\r
-The nodes on either side of the portal may actually be clusters,\r
-not leafs, so all contents should be ored together\r
-=============\r
-*/\r
-qboolean Portal_VisFlood (portal_t *p)\r
-{\r
-       int             c1, c2;\r
-\r
-       if (!p->onnode)\r
-               return false;   // to global outsideleaf\r
-\r
-       c1 = ClusterContents(p->nodes[0]);\r
-       c2 = ClusterContents(p->nodes[1]);\r
-\r
-       if (!VisibleContents (c1^c2))\r
-               return true;\r
-\r
-       if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))\r
-               c1 = 0;\r
-       if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))\r
-               c2 = 0;\r
-\r
-       if ( (c1|c2) & CONTENTS_SOLID )\r
-               return false;           // can't see through solid\r
-\r
-       if (! (c1 ^ c2))\r
-               return true;            // identical on both sides\r
-\r
-       if (!VisibleContents (c1^c2))\r
-               return true;\r
-       return false;\r
-}\r
-\r
-\r
-/*\r
-===============\r
-Portal_EntityFlood\r
-\r
-The entity flood determines which areas are\r
-"outside" on the map, which are then filled in.\r
-Flowing from side s to side !s\r
-===============\r
-*/\r
-qboolean Portal_EntityFlood (portal_t *p, int s)\r
-{\r
-       if (p->nodes[0]->planenum != PLANENUM_LEAF\r
-               || p->nodes[1]->planenum != PLANENUM_LEAF)\r
-               Error ("Portal_EntityFlood: not a leaf");\r
-\r
-       // can never cross to a solid \r
-       if ( (p->nodes[0]->contents & CONTENTS_SOLID)\r
-       || (p->nodes[1]->contents & CONTENTS_SOLID) )\r
-               return false;\r
-\r
-       // can flood through everything else\r
-       return true;\r
-}\r
-\r
-\r
-//=============================================================================\r
-\r
-int            c_tinyportals;\r
-\r
-/*\r
-=============\r
-AddPortalToNodes\r
-=============\r
-*/\r
-void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)\r
-{\r
-       if (p->nodes[0] || p->nodes[1])\r
-               Error ("AddPortalToNode: allready included");\r
-\r
-       p->nodes[0] = front;\r
-       p->next[0] = front->portals;\r
-       front->portals = p;\r
-       \r
-       p->nodes[1] = back;\r
-       p->next[1] = back->portals;\r
-       back->portals = p;\r
-}\r
-\r
-\r
-/*\r
-=============\r
-RemovePortalFromNode\r
-=============\r
-*/\r
-void RemovePortalFromNode (portal_t *portal, node_t *l)\r
-{\r
-       portal_t        **pp, *t;\r
-       \r
-// remove reference to the current portal\r
-       pp = &l->portals;\r
-       while (1)\r
-       {\r
-               t = *pp;\r
-               if (!t)\r
-                       Error ("RemovePortalFromNode: portal not in leaf");     \r
-\r
-               if ( t == portal )\r
-                       break;\r
-\r
-               if (t->nodes[0] == l)\r
-                       pp = &t->next[0];\r
-               else if (t->nodes[1] == l)\r
-                       pp = &t->next[1];\r
-               else\r
-                       Error ("RemovePortalFromNode: portal not bounding leaf");\r
-       }\r
-       \r
-       if (portal->nodes[0] == l)\r
-       {\r
-               *pp = portal->next[0];\r
-               portal->nodes[0] = NULL;\r
-       }\r
-       else if (portal->nodes[1] == l)\r
-       {\r
-               *pp = portal->next[1];  \r
-               portal->nodes[1] = NULL;\r
-       }\r
-}\r
-\r
-//============================================================================\r
-\r
-void PrintPortal (portal_t *p)\r
-{\r
-       int                     i;\r
-       winding_t       *w;\r
-       \r
-       w = p->winding;\r
-       for (i=0 ; i<w->numpoints ; i++)\r
-               Sys_Printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]\r
-               , w->p[i][1], w->p[i][2]);\r
-}\r
-\r
-/*\r
-================\r
-MakeHeadnodePortals\r
-\r
-The created portals will face the global outside_node\r
-================\r
-*/\r
-#define        SIDESPACE       8\r
-void MakeHeadnodePortals (tree_t *tree)\r
-{\r
-       vec3_t          bounds[2];\r
-       int                     i, j, n;\r
-       portal_t        *p, *portals[6];\r
-       plane_t         bplanes[6], *pl;\r
-       node_t *node;\r
-\r
-       node = tree->headnode;\r
-\r
-// pad with some space so there will never be null volume leafs\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               bounds[0][i] = tree->mins[i] - SIDESPACE;\r
-               bounds[1][i] = tree->maxs[i] + SIDESPACE;\r
-       }\r
-       \r
-       tree->outside_node.planenum = PLANENUM_LEAF;\r
-       tree->outside_node.brushlist = NULL;\r
-       tree->outside_node.portals = NULL;\r
-       tree->outside_node.contents = 0;\r
-\r
-       for (i=0 ; i<3 ; i++)\r
-               for (j=0 ; j<2 ; j++)\r
-               {\r
-                       n = j*3 + i;\r
-\r
-                       p = AllocPortal ();\r
-                       portals[n] = p;\r
-                       \r
-                       pl = &bplanes[n];\r
-                       memset (pl, 0, sizeof(*pl));\r
-                       if (j)\r
-                       {\r
-                               pl->normal[i] = -1;\r
-                               pl->dist = -bounds[j][i];\r
-                       }\r
-                       else\r
-                       {\r
-                               pl->normal[i] = 1;\r
-                               pl->dist = bounds[j][i];\r
-                       }\r
-                       p->plane = *pl;\r
-                       p->winding = BaseWindingForPlane (pl->normal, pl->dist);\r
-                       AddPortalToNodes (p, node, &tree->outside_node);\r
-               }\r
-               \r
-// clip the basewindings by all the other planes\r
-       for (i=0 ; i<6 ; i++)\r
-       {\r
-               for (j=0 ; j<6 ; j++)\r
-               {\r
-                       if (j == i)\r
-                               continue;\r
-                       ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);\r
-               }\r
-       }\r
-}\r
-\r
-//===================================================\r
-\r
-\r
-/*\r
-================\r
-BaseWindingForNode\r
-================\r
-*/\r
-#define        BASE_WINDING_EPSILON    0.001\r
-#define        SPLIT_WINDING_EPSILON   0.001\r
-\r
-winding_t      *BaseWindingForNode (node_t *node)\r
-{\r
-       winding_t       *w;\r
-       node_t          *n;\r
-       plane_t         *plane;\r
-       vec3_t          normal;\r
-       vec_t           dist;\r
-\r
-       w = BaseWindingForPlane (mapplanes[node->planenum].normal\r
-               , mapplanes[node->planenum].dist);\r
-\r
-       // clip by all the parents\r
-       for (n=node->parent ; n && w ; )\r
-       {\r
-               plane = &mapplanes[n->planenum];\r
-\r
-               if (n->children[0] == node)\r
-               {       // take front\r
-                       ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);\r
-               }\r
-               else\r
-               {       // take back\r
-                       VectorSubtract (vec3_origin, plane->normal, normal);\r
-                       dist = -plane->dist;\r
-                       ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);\r
-               }\r
-               node = n;\r
-               n = n->parent;\r
-       }\r
-\r
-       return w;\r
-}\r
-\r
-//============================================================\r
-\r
-qboolean WindingIsTiny (winding_t *w);\r
-\r
-/*\r
-==================\r
-MakeNodePortal\r
-\r
-create the new portal by taking the full plane winding for the cutting plane\r
-and clipping it by all of parents of this node\r
-==================\r
-*/\r
-void MakeNodePortal (node_t *node)\r
-{\r
-       portal_t        *new_portal, *p;\r
-       winding_t       *w;\r
-       vec3_t          normal;\r
-       float           dist;\r
-       int                     side;\r
-\r
-       w = BaseWindingForNode (node);\r
-\r
-       // clip the portal by all the other portals in the node\r
-       for (p = node->portals ; p && w; p = p->next[side])     \r
-       {\r
-               if (p->nodes[0] == node)\r
-               {\r
-                       side = 0;\r
-                       VectorCopy (p->plane.normal, normal);\r
-                       dist = p->plane.dist;\r
-               }\r
-               else if (p->nodes[1] == node)\r
-               {\r
-                       side = 1;\r
-                       VectorSubtract (vec3_origin, p->plane.normal, normal);\r
-                       dist = -p->plane.dist;\r
-               }\r
-               else\r
-                       Error ("CutNodePortals_r: mislinked portal");\r
-\r
-               ChopWindingInPlace (&w, normal, dist, 0.1);\r
-       }\r
-\r
-       if (!w)\r
-       {\r
-               return;\r
-       }\r
-\r
-       if (WindingIsTiny (w))\r
-       {\r
-               c_tinyportals++;\r
-               FreeWinding (w);\r
-               return;\r
-       }\r
-\r
-\r
-       new_portal = AllocPortal ();\r
-       new_portal->plane = mapplanes[node->planenum];\r
-       new_portal->onnode = node;\r
-       new_portal->winding = w;        \r
-       AddPortalToNodes (new_portal, node->children[0], node->children[1]);\r
-}\r
-\r
-\r
-/*\r
-==============\r
-SplitNodePortals\r
-\r
-Move or split the portals that bound node so that the node's\r
-children have portals instead of node.\r
-==============\r
-*/\r
-void SplitNodePortals (node_t *node)\r
-{\r
-       portal_t        *p, *next_portal, *new_portal;\r
-       node_t          *f, *b, *other_node;\r
-       int                     side;\r
-       plane_t         *plane;\r
-       winding_t       *frontwinding, *backwinding;\r
-\r
-       plane = &mapplanes[node->planenum];\r
-       f = node->children[0];\r
-       b = node->children[1];\r
-\r
-       for (p = node->portals ; p ; p = next_portal)   \r
-       {\r
-               if (p->nodes[0] == node)\r
-                       side = 0;\r
-               else if (p->nodes[1] == node)\r
-                       side = 1;\r
-               else\r
-                       Error ("CutNodePortals_r: mislinked portal");\r
-               next_portal = p->next[side];\r
-\r
-               other_node = p->nodes[!side];\r
-               RemovePortalFromNode (p, p->nodes[0]);\r
-               RemovePortalFromNode (p, p->nodes[1]);\r
-\r
-//\r
-// cut the portal into two portals, one on each side of the cut plane\r
-//\r
-               ClipWindingEpsilon (p->winding, plane->normal, plane->dist,\r
-                       SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);\r
-\r
-               if (frontwinding && WindingIsTiny(frontwinding))\r
-               {\r
-                       FreeWinding (frontwinding);\r
-                       frontwinding = NULL;\r
-                       c_tinyportals++;\r
-               }\r
-\r
-               if (backwinding && WindingIsTiny(backwinding))\r
-               {\r
-                       FreeWinding (backwinding);\r
-                       backwinding = NULL;\r
-                       c_tinyportals++;\r
-               }\r
-\r
-               if (!frontwinding && !backwinding)\r
-               {       // tiny windings on both sides\r
-                       continue;\r
-               }\r
-\r
-               if (!frontwinding)\r
-               {\r
-                       FreeWinding (backwinding);\r
-                       if (side == 0)\r
-                               AddPortalToNodes (p, b, other_node);\r
-                       else\r
-                               AddPortalToNodes (p, other_node, b);\r
-                       continue;\r
-               }\r
-               if (!backwinding)\r
-               {\r
-                       FreeWinding (frontwinding);\r
-                       if (side == 0)\r
-                               AddPortalToNodes (p, f, other_node);\r
-                       else\r
-                               AddPortalToNodes (p, other_node, f);\r
-                       continue;\r
-               }\r
-               \r
-       // the winding is split\r
-               new_portal = AllocPortal ();\r
-               *new_portal = *p;\r
-               new_portal->winding = backwinding;\r
-               FreeWinding (p->winding);\r
-               p->winding = frontwinding;\r
-\r
-               if (side == 0)\r
-               {\r
-                       AddPortalToNodes (p, f, other_node);\r
-                       AddPortalToNodes (new_portal, b, other_node);\r
-               }\r
-               else\r
-               {\r
-                       AddPortalToNodes (p, other_node, f);\r
-                       AddPortalToNodes (new_portal, other_node, b);\r
-               }\r
-       }\r
-\r
-       node->portals = NULL;\r
-}\r
-\r
-\r
-/*\r
-================\r
-CalcNodeBounds\r
-================\r
-*/\r
-void CalcNodeBounds (node_t *node)\r
-{\r
-       portal_t        *p;\r
-       int                     s;\r
-       int                     i;\r
-\r
-       // calc mins/maxs for both leafs and nodes\r
-       ClearBounds (node->mins, node->maxs);\r
-       for (p = node->portals ; p ; p = p->next[s])    \r
-       {\r
-               s = (p->nodes[1] == node);\r
-               for (i=0 ; i<p->winding->numpoints ; i++)\r
-                       AddPointToBounds (p->winding->p[i], node->mins, node->maxs);\r
-       }\r
-}\r
-\r
-\r
-/*\r
-==================\r
-MakeTreePortals_r\r
-==================\r
-*/\r
-void MakeTreePortals_r (node_t *node)\r
-{\r
-       int             i;\r
-\r
-       CalcNodeBounds (node);\r
-       if (node->mins[0] >= node->maxs[0])\r
-       {\r
-               Sys_Printf ("WARNING: node without a volume\n");\r
-       }\r
-\r
-       for (i=0 ; i<3 ; i++)\r
-       {\r
-               if (node->mins[i] < -8000 || node->maxs[i] > 8000)\r
-               {\r
-                       Sys_Printf ("WARNING: node with unbounded volume\n");\r
-                       break;\r
-               }\r
-       }\r
-       if (node->planenum == PLANENUM_LEAF)\r
-               return;\r
-\r
-       MakeNodePortal (node);\r
-       SplitNodePortals (node);\r
-\r
-       MakeTreePortals_r (node->children[0]);\r
-       MakeTreePortals_r (node->children[1]);\r
-}\r
-\r
-/*\r
-==================\r
-MakeTreePortals\r
-==================\r
-*/\r
-void MakeTreePortals (tree_t *tree)\r
-{\r
-       MakeHeadnodePortals (tree);\r
-       MakeTreePortals_r (tree->headnode);\r
-}\r
-\r
-/*\r
-=========================================================\r
-\r
-FLOOD ENTITIES\r
-\r
-=========================================================\r
-*/\r
-\r
-/*\r
-=============\r
-FloodPortals_r\r
-=============\r
-*/\r
-void FloodPortals_r (node_t *node, int dist)\r
-{\r
-       portal_t        *p;\r
-       int                     s;\r
-\r
-       node->occupied = dist;\r
-\r
-       for (p=node->portals ; p ; p = p->next[s])\r
-       {\r
-               s = (p->nodes[1] == node);\r
-\r
-               if (p->nodes[!s]->occupied)\r
-                       continue;\r
-\r
-               if (!Portal_EntityFlood (p, s))\r
-                       continue;\r
-\r
-               FloodPortals_r (p->nodes[!s], dist+1);\r
-       }\r
-}\r
-\r
-/*\r
-=============\r
-PlaceOccupant\r
-=============\r
-*/\r
-qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)\r
-{\r
-       node_t  *node;\r
-       vec_t   d;\r
-       plane_t *plane;\r
-\r
-       // find the leaf to start in\r
-       node = headnode;\r
-       while (node->planenum != PLANENUM_LEAF)\r
-       {\r
-               plane = &mapplanes[node->planenum];\r
-               d = DotProduct (origin, plane->normal) - plane->dist;\r
-               if (d >= 0)\r
-                       node = node->children[0];\r
-               else\r
-                       node = node->children[1];\r
-       }\r
-\r
-       if (node->contents == CONTENTS_SOLID)\r
-               return false;\r
-       node->occupant = occupant;\r
-\r
-       FloodPortals_r (node, 1);\r
-\r
-       return true;\r
-}\r
-\r
-/*\r
-=============\r
-FloodEntities\r
-\r
-Marks all nodes that can be reached by entites\r
-=============\r
-*/\r
-qboolean FloodEntities (tree_t *tree)\r
-{\r
-       int             i;\r
-       vec3_t  origin;\r
-       char    *cl;\r
-       qboolean        inside;\r
-       node_t *headnode;\r
-\r
-       headnode = tree->headnode;\r
-       Sys_FPrintf( SYS_VRB, "--- FloodEntities ---\n");\r
-       inside = false;\r
-       tree->outside_node.occupied = 0;\r
-\r
-       for (i=1 ; i<num_entities ; i++)\r
-       {\r
-               GetVectorForKey (&entities[i], "origin", origin);\r
-               if (VectorCompare(origin, vec3_origin))\r
-                       continue;\r
-\r
-               cl = ValueForKey (&entities[i], "classname");\r
-               origin[2] += 1; // so objects on floor are ok\r
-\r
-               // nudge playerstart around if needed so clipping hulls allways\r
-               // have a vlaid point\r
-               if (!strcmp (cl, "info_player_start"))\r
-               {\r
-                       int     x, y;\r
-\r
-                       for (x=-16 ; x<=16 ; x += 16)\r
-                       {\r
-                               for (y=-16 ; y<=16 ; y += 16)\r
-                               {\r
-                                       origin[0] += x;\r
-                                       origin[1] += y;\r
-                                       if (PlaceOccupant (headnode, origin, &entities[i]))\r
-                                       {\r
-                                               inside = true;\r
-                                               goto gotit;\r
-                                       }\r
-                                       origin[0] -= x;\r
-                                       origin[1] -= y;\r
-                               }\r
-                       }\r
-gotit: ;\r
-               }\r
-               else\r
-               {\r
-                       if (PlaceOccupant (headnode, origin, &entities[i]))\r
-                               inside = true;\r
-               }\r
-       }\r
-\r
-       if (!inside)\r
-       {\r
-               Sys_FPrintf( SYS_VRB, "no entities in open -- no filling\n");\r
-       }\r
-       else if (tree->outside_node.occupied)\r
-       {\r
-               Sys_FPrintf( SYS_VRB, "entity reached from outside -- no filling\n");\r
-       }\r
-\r
-       return (qboolean)(inside && !tree->outside_node.occupied);\r
-}\r
-\r
-/*\r
-=========================================================\r
-\r
-FLOOD AREAS\r
-\r
-=========================================================\r
-*/\r
-\r
-int            c_areas;\r
-\r
-/*\r
-=============\r
-FloodAreas_r\r
-=============\r
-*/\r
-void FloodAreas_r (node_t *node)\r
-{\r
-       portal_t        *p;\r
-       int                     s;\r
-       bspbrush_t      *b;\r
-       entity_t        *e;\r
-\r
-       if (node->contents == CONTENTS_AREAPORTAL)\r
-       {\r
-               // this node is part of an area portal\r
-               b = node->brushlist;\r
-               e = &entities[b->original->entitynum];\r
-\r
-               // if the current area has allready touched this\r
-               // portal, we are done\r
-               if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)\r
-                       return;\r
-\r
-               // note the current area as bounding the portal\r
-               if (e->portalareas[1])\r
-               {\r
-                       Sys_Printf ("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum);\r
-                       return;\r
-               }\r
-               if (e->portalareas[0])\r
-                       e->portalareas[1] = c_areas;\r
-               else\r
-                       e->portalareas[0] = c_areas;\r
-\r
-               return;\r
-       }\r
-\r
-       if (node->area)\r
-               return;         // allready got it\r
-       node->area = c_areas;\r
-\r
-       for (p=node->portals ; p ; p = p->next[s])\r
-       {\r
-               s = (p->nodes[1] == node);\r
-#if 0\r
-               if (p->nodes[!s]->occupied)\r
-                       continue;\r
-#endif\r
-               if (!Portal_EntityFlood (p, s))\r
-                       continue;\r
-\r
-               FloodAreas_r (p->nodes[!s]);\r
-       }\r
-}\r
-\r
-/*\r
-=============\r
-FindAreas_r\r
-\r
-Just decend the tree, and for each node that hasn't had an\r
-area set, flood fill out from there\r
-=============\r
-*/\r
-void FindAreas_r (node_t *node)\r
-{\r
-       if (node->planenum != PLANENUM_LEAF)\r
-       {\r
-               FindAreas_r (node->children[0]);\r
-               FindAreas_r (node->children[1]);\r
-               return;\r
-       }\r
-\r
-       if (node->area)\r
-               return;         // allready got it\r
-\r
-       if (node->contents & CONTENTS_SOLID)\r
-               return;\r
-\r
-       if (!node->occupied)\r
-               return;                 // not reachable by entities\r
-\r
-       // area portals are allways only flooded into, never\r
-       // out of\r
-       if (node->contents == CONTENTS_AREAPORTAL)\r
-               return;\r
-\r
-       c_areas++;\r
-       FloodAreas_r (node);\r
-}\r
-\r
-/*\r
-=============\r
-SetAreaPortalAreas_r\r
-\r
-Just decend the tree, and for each node that hasn't had an\r
-area set, flood fill out from there\r
-=============\r
-*/\r
-void SetAreaPortalAreas_r (node_t *node)\r
-{\r
-       bspbrush_t      *b;\r
-       entity_t        *e;\r
-\r
-       if (node->planenum != PLANENUM_LEAF)\r
-       {\r
-               SetAreaPortalAreas_r (node->children[0]);\r
-               SetAreaPortalAreas_r (node->children[1]);\r
-               return;\r
-       }\r
-\r
-       if (node->contents == CONTENTS_AREAPORTAL)\r
-       {\r
-               if (node->area)\r
-                       return;         // allready set\r
-\r
-               b = node->brushlist;\r
-               e = &entities[b->original->entitynum];\r
-               node->area = e->portalareas[0];\r
-               if (!e->portalareas[1])\r
-               {\r
-                       Sys_Printf ("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum);\r
-                       return;\r
-               }\r
-       }\r
-}\r
-\r
-/*\r
-=============\r
-EmitAreaPortals\r
-\r
-=============\r
-*/\r
-void EmitAreaPortals (node_t *headnode)\r
-{\r
-       int                             i, j;\r
-       entity_t                *e;\r
-       dareaportal_t   *dp;\r
-\r
-       if (c_areas > MAX_MAP_AREAS)\r
-               Error ("MAX_MAP_AREAS");\r
-       numareas = c_areas+1;\r
-       numareaportals = 1;             // leave 0 as an error\r
-\r
-       for (i=1 ; i<=c_areas ; i++)\r
-       {\r
-               dareas[i].firstareaportal = numareaportals;\r
-               for (j=0 ; j<num_entities ; j++)\r
-               {\r
-                       e = &entities[j];\r
-                       if (!e->areaportalnum)\r
-                               continue;\r
-                       dp = &dareaportals[numareaportals];\r
-                       if (e->portalareas[0] == i)\r
-                       {\r
-                               dp->portalnum = e->areaportalnum;\r
-                               dp->otherarea = e->portalareas[1];\r
-                               numareaportals++;\r
-                       }\r
-                       else if (e->portalareas[1] == i)\r
-                       {\r
-                               dp->portalnum = e->areaportalnum;\r
-                               dp->otherarea = e->portalareas[0];\r
-                               numareaportals++;\r
-                       }\r
-               }\r
-               dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;\r
-       }\r
-\r
-       Sys_FPrintf( SYS_VRB, "%5i numareas\n", numareas);\r
-       Sys_FPrintf( SYS_VRB, "%5i numareaportals\n", numareaportals);\r
-}\r
-\r
-/*\r
-=============\r
-FloodAreas\r
-\r
-Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL\r
-=============\r
-*/\r
-void FloodAreas (tree_t *tree)\r
-{\r
-       Sys_FPrintf( SYS_VRB, "--- FloodAreas ---\n");\r
-       FindAreas_r (tree->headnode);\r
-       SetAreaPortalAreas_r (tree->headnode);\r
-       Sys_FPrintf( SYS_VRB, "%5i areas\n", c_areas);\r
-}\r
-\r
-//======================================================\r
-\r
-int            c_outside;\r
-int            c_inside;\r
-int            c_solid;\r
-\r
-void FillOutside_r (node_t *node)\r
-{\r
-       if (node->planenum != PLANENUM_LEAF)\r
-       {\r
-               FillOutside_r (node->children[0]);\r
-               FillOutside_r (node->children[1]);\r
-               return;\r
-       }\r
-\r
-       // anything not reachable by an entity\r
-       // can be filled away\r
-       if (!node->occupied)\r
-       {\r
-               if (node->contents != CONTENTS_SOLID)\r
-               {\r
-                       c_outside++;\r
-                       node->contents = CONTENTS_SOLID;\r
-               }\r
-               else\r
-                       c_solid++;\r
-       }\r
-       else\r
-               c_inside++;\r
-\r
-}\r
-\r
-/*\r
-=============\r
-FillOutside\r
-\r
-Fill all nodes that can't be reached by entities\r
-=============\r
-*/\r
-void FillOutside (node_t *headnode)\r
-{\r
-       c_outside = 0;\r
-       c_inside = 0;\r
-       c_solid = 0;\r
-       Sys_FPrintf( SYS_VRB, "--- FillOutside ---\n");\r
-       FillOutside_r (headnode);\r
-       Sys_FPrintf( SYS_VRB, "%5i solid leafs\n", c_solid);\r
-       Sys_FPrintf( SYS_VRB, "%5i leafs filled\n", c_outside);\r
-       Sys_FPrintf( SYS_VRB, "%5i inside leafs\n", c_inside);\r
-}\r
-\r
-\r
-//==============================================================\r
-\r
-/*\r
-============\r
-FindPortalSide\r
-\r
-Finds a brush side to use for texturing the given portal\r
-============\r
-*/\r
-void FindPortalSide (portal_t *p)\r
-{\r
-       int                     viscontents;\r
-       bspbrush_t      *bb;\r
-       mapbrush_t      *brush;\r
-       node_t          *n;\r
-       int                     i,j;\r
-       int                     planenum;\r
-       side_t          *side, *bestside;\r
-       float           dot, bestdot;\r
-       plane_t         *p1, *p2;\r
-\r
-       // decide which content change is strongest\r
-       // solid > lava > water, etc\r
-       viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);\r
-       if (!viscontents)\r
-               return;\r
-\r
-       planenum = p->onnode->planenum;\r
-       bestside = NULL;\r
-       bestdot = 0;\r
-\r
-       for (j=0 ; j<2 ; j++)\r
-       {\r
-               n = p->nodes[j];\r
-               p1 = &mapplanes[p->onnode->planenum];\r
-               for (bb=n->brushlist ; bb ; bb=bb->next)\r
-               {\r
-                       brush = bb->original;\r
-                       if ( !(brush->contents & viscontents) )\r
-                               continue;\r
-                       for (i=0 ; i<brush->numsides ; i++)\r
-                       {\r
-                               side = &brush->original_sides[i];\r
-                               if (side->bevel)\r
-                                       continue;\r
-                               if (side->texinfo == TEXINFO_NODE)\r
-                                       continue;               // non-visible\r
-                               if ((side->planenum&~1) == planenum)\r
-                               {       // exact match\r
-                                       bestside = &brush->original_sides[i];\r
-                                       goto gotit;\r
-                               }\r
-                               // see how close the match is\r
-                               p2 = &mapplanes[side->planenum&~1];\r
-                               dot = DotProduct (p1->normal, p2->normal);\r
-                               if (dot > bestdot)\r
-                               {\r
-                                       bestdot = dot;\r
-                                       bestside = side;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-gotit:\r
-       if (!bestside)\r
-               Sys_FPrintf( SYS_VRB, "WARNING: side not found for portal\n");\r
-\r
-       p->sidefound = true;\r
-       p->side = bestside;\r
-}\r
-\r
-\r
-/*\r
-===============\r
-MarkVisibleSides_r\r
-\r
-===============\r
-*/\r
-void MarkVisibleSides_r (node_t *node)\r
-{\r
-       portal_t        *p;\r
-       int                     s;\r
-\r
-       if (node->planenum != PLANENUM_LEAF)\r
-       {\r
-               MarkVisibleSides_r (node->children[0]);\r
-               MarkVisibleSides_r (node->children[1]);\r
-               return;\r
-       }\r
-\r
-       // empty leafs are never boundary leafs\r
-       if (!node->contents)\r
-               return;\r
-\r
-       // see if there is a visible face\r
-       for (p=node->portals ; p ; p = p->next[!s])\r
-       {\r
-               s = (p->nodes[0] == node);\r
-               if (!p->onnode)\r
-                       continue;               // edge of world\r
-               if (!p->sidefound)\r
-                       FindPortalSide (p);\r
-               if (p->side)\r
-                       p->side->visible = true;\r
-       }\r
-\r
-}\r
-\r
-/*\r
-=============\r
-MarkVisibleSides\r
-\r
-=============\r
-*/\r
-void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush)\r
-{\r
-       int             i, j;\r
-       mapbrush_t      *mb;\r
-       int             numsides;\r
-\r
-       Sys_FPrintf( SYS_VRB, "--- MarkVisibleSides ---\n");\r
-\r
-       // clear all the visible flags\r
-       for (i=startbrush ; i<endbrush ; i++)\r
-       {\r
-               mb = &mapbrushes[i];\r
-\r
-               numsides = mb->numsides;\r
-               for (j=0 ; j<numsides ; j++)\r
-                       mb->original_sides[j].visible = false;\r
-       }\r
-\r
-       // set visible flags on the sides that are used by portals\r
-       MarkVisibleSides_r (tree->headnode);\r
-}\r
-\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "qbsp.h"
+
+
+int            c_active_portals;
+int            c_peak_portals;
+int            c_boundary;
+int            c_boundary_sides;
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal (void)
+{
+       portal_t        *p;
+       
+       if (numthreads == 1)
+               c_active_portals++;
+       if (c_active_portals > c_peak_portals)
+               c_peak_portals = c_active_portals;
+       
+       p = malloc (sizeof(portal_t));
+       memset (p, 0, sizeof(portal_t));
+       
+       return p;
+}
+
+void FreePortal (portal_t *p)
+{
+       if (p->winding)
+               FreeWinding (p->winding);
+       if (numthreads == 1)
+               c_active_portals--;
+       free (p);
+}
+
+//==============================================================
+
+/*
+==============
+VisibleContents
+
+Returns the single content bit of the
+strongest visible content present
+==============
+*/
+int VisibleContents (int contents)
+{
+       int             i;
+
+       for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
+               if (contents & i )
+                       return i;
+
+       return 0;
+}
+
+
+/*
+===============
+ClusterContents
+===============
+*/
+int ClusterContents (node_t *node)
+{
+       int             c1, c2, c;
+
+       if (node->planenum == PLANENUM_LEAF)
+               return node->contents;
+
+       c1 = ClusterContents(node->children[0]);
+       c2 = ClusterContents(node->children[1]);
+       c = c1|c2;
+
+       // a cluster may include some solid detail areas, but
+       // still be seen into
+       if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
+               c &= ~CONTENTS_SOLID;
+       return c;
+}
+
+/*
+=============
+Portal_VisFlood
+
+Returns true if the portal is empty or translucent, allowing
+the PVS calculation to see through it.
+The nodes on either side of the portal may actually be clusters,
+not leafs, so all contents should be ored together
+=============
+*/
+qboolean Portal_VisFlood (portal_t *p)
+{
+       int             c1, c2;
+
+       if (!p->onnode)
+               return false;   // to global outsideleaf
+
+       c1 = ClusterContents(p->nodes[0]);
+       c2 = ClusterContents(p->nodes[1]);
+
+       if (!VisibleContents (c1^c2))
+               return true;
+
+       if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+               c1 = 0;
+       if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+               c2 = 0;
+
+       if ( (c1|c2) & CONTENTS_SOLID )
+               return false;           // can't see through solid
+
+       if (! (c1 ^ c2))
+               return true;            // identical on both sides
+
+       if (!VisibleContents (c1^c2))
+               return true;
+       return false;
+}
+
+
+/*
+===============
+Portal_EntityFlood
+
+The entity flood determines which areas are
+"outside" on the map, which are then filled in.
+Flowing from side s to side !s
+===============
+*/
+qboolean Portal_EntityFlood (portal_t *p, int s)
+{
+       if (p->nodes[0]->planenum != PLANENUM_LEAF
+               || p->nodes[1]->planenum != PLANENUM_LEAF)
+               Error ("Portal_EntityFlood: not a leaf");
+
+       // can never cross to a solid 
+       if ( (p->nodes[0]->contents & CONTENTS_SOLID)
+       || (p->nodes[1]->contents & CONTENTS_SOLID) )
+               return false;
+
+       // can flood through everything else
+       return true;
+}
+
+
+//=============================================================================
+
+int            c_tinyportals;
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
+{
+       if (p->nodes[0] || p->nodes[1])
+               Error ("AddPortalToNode: allready included");
+
+       p->nodes[0] = front;
+       p->next[0] = front->portals;
+       front->portals = p;
+       
+       p->nodes[1] = back;
+       p->next[1] = back->portals;
+       back->portals = p;
+}
+
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+void RemovePortalFromNode (portal_t *portal, node_t *l)
+{
+       portal_t        **pp, *t;
+       
+// remove reference to the current portal
+       pp = &l->portals;
+       while (1)
+       {
+               t = *pp;
+               if (!t)
+                       Error ("RemovePortalFromNode: portal not in leaf");     
+
+               if ( t == portal )
+                       break;
+
+               if (t->nodes[0] == l)
+                       pp = &t->next[0];
+               else if (t->nodes[1] == l)
+                       pp = &t->next[1];
+               else
+                       Error ("RemovePortalFromNode: portal not bounding leaf");
+       }
+       
+       if (portal->nodes[0] == l)
+       {
+               *pp = portal->next[0];
+               portal->nodes[0] = NULL;
+       }
+       else if (portal->nodes[1] == l)
+       {
+               *pp = portal->next[1];  
+               portal->nodes[1] = NULL;
+       }
+}
+
+//============================================================================
+
+void PrintPortal (portal_t *p)
+{
+       int                     i;
+       winding_t       *w;
+       
+       w = p->winding;
+       for (i=0 ; i<w->numpoints ; i++)
+               Sys_Printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
+               , w->p[i][1], w->p[i][2]);
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+#define        SIDESPACE       8
+void MakeHeadnodePortals (tree_t *tree)
+{
+       vec3_t          bounds[2];
+       int                     i, j, n;
+       portal_t        *p, *portals[6];
+       plane_t         bplanes[6], *pl;
+       node_t *node;
+
+       node = tree->headnode;
+
+// pad with some space so there will never be null volume leafs
+       for (i=0 ; i<3 ; i++)
+       {
+               bounds[0][i] = tree->mins[i] - SIDESPACE;
+               bounds[1][i] = tree->maxs[i] + SIDESPACE;
+       }
+       
+       tree->outside_node.planenum = PLANENUM_LEAF;
+       tree->outside_node.brushlist = NULL;
+       tree->outside_node.portals = NULL;
+       tree->outside_node.contents = 0;
+
+       for (i=0 ; i<3 ; i++)
+               for (j=0 ; j<2 ; j++)
+               {
+                       n = j*3 + i;
+
+                       p = AllocPortal ();
+                       portals[n] = p;
+                       
+                       pl = &bplanes[n];
+                       memset (pl, 0, sizeof(*pl));
+                       if (j)
+                       {
+                               pl->normal[i] = -1;
+                               pl->dist = -bounds[j][i];
+                       }
+                       else
+                       {
+                               pl->normal[i] = 1;
+                               pl->dist = bounds[j][i];
+                       }
+                       p->plane = *pl;
+                       p->winding = BaseWindingForPlane (pl->normal, pl->dist);
+                       AddPortalToNodes (p, node, &tree->outside_node);
+               }
+               
+// clip the basewindings by all the other planes
+       for (i=0 ; i<6 ; i++)
+       {
+               for (j=0 ; j<6 ; j++)
+               {
+                       if (j == i)
+                               continue;
+                       ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
+               }
+       }
+}
+
+//===================================================
+
+
+/*
+================
+BaseWindingForNode
+================
+*/
+#define        BASE_WINDING_EPSILON    0.001
+#define        SPLIT_WINDING_EPSILON   0.001
+
+winding_t      *BaseWindingForNode (node_t *node)
+{
+       winding_t       *w;
+       node_t          *n;
+       plane_t         *plane;
+       vec3_t          normal;
+       vec_t           dist;
+
+       w = BaseWindingForPlane (mapplanes[node->planenum].normal
+               , mapplanes[node->planenum].dist);
+
+       // clip by all the parents
+       for (n=node->parent ; n && w ; )
+       {
+               plane = &mapplanes[n->planenum];
+
+               if (n->children[0] == node)
+               {       // take front
+                       ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
+               }
+               else
+               {       // take back
+                       VectorSubtract (vec3_origin, plane->normal, normal);
+                       dist = -plane->dist;
+                       ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
+               }
+               node = n;
+               n = n->parent;
+       }
+
+       return w;
+}
+
+//============================================================
+
+qboolean WindingIsTiny (winding_t *w);
+
+/*
+==================
+MakeNodePortal
+
+create the new portal by taking the full plane winding for the cutting plane
+and clipping it by all of parents of this node
+==================
+*/
+void MakeNodePortal (node_t *node)
+{
+       portal_t        *new_portal, *p;
+       winding_t       *w;
+       vec3_t          normal;
+       float           dist;
+       int                     side;
+
+       w = BaseWindingForNode (node);
+
+       // clip the portal by all the other portals in the node
+       for (p = node->portals ; p && w; p = p->next[side])     
+       {
+               if (p->nodes[0] == node)
+               {
+                       side = 0;
+                       VectorCopy (p->plane.normal, normal);
+                       dist = p->plane.dist;
+               }
+               else if (p->nodes[1] == node)
+               {
+                       side = 1;
+                       VectorSubtract (vec3_origin, p->plane.normal, normal);
+                       dist = -p->plane.dist;
+               }
+               else
+                       Error ("CutNodePortals_r: mislinked portal");
+
+               ChopWindingInPlace (&w, normal, dist, 0.1);
+       }
+
+       if (!w)
+       {
+               return;
+       }
+
+       if (WindingIsTiny (w))
+       {
+               c_tinyportals++;
+               FreeWinding (w);
+               return;
+       }
+
+
+       new_portal = AllocPortal ();
+       new_portal->plane = mapplanes[node->planenum];
+       new_portal->onnode = node;
+       new_portal->winding = w;        
+       AddPortalToNodes (new_portal, node->children[0], node->children[1]);
+}
+
+
+/*
+==============
+SplitNodePortals
+
+Move or split the portals that bound node so that the node's
+children have portals instead of node.
+==============
+*/
+void SplitNodePortals (node_t *node)
+{
+       portal_t        *p, *next_portal, *new_portal;
+       node_t          *f, *b, *other_node;
+       int                     side;
+       plane_t         *plane;
+       winding_t       *frontwinding, *backwinding;
+
+       plane = &mapplanes[node->planenum];
+       f = node->children[0];
+       b = node->children[1];
+
+       for (p = node->portals ; p ; p = next_portal)   
+       {
+               if (p->nodes[0] == node)
+                       side = 0;
+               else if (p->nodes[1] == node)
+                       side = 1;
+               else
+                       Error ("CutNodePortals_r: mislinked portal");
+               next_portal = p->next[side];
+
+               other_node = p->nodes[!side];
+               RemovePortalFromNode (p, p->nodes[0]);
+               RemovePortalFromNode (p, p->nodes[1]);
+
+//
+// cut the portal into two portals, one on each side of the cut plane
+//
+               ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
+                       SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
+
+               if (frontwinding && WindingIsTiny(frontwinding))
+               {
+                       FreeWinding (frontwinding);
+                       frontwinding = NULL;
+                       c_tinyportals++;
+               }
+
+               if (backwinding && WindingIsTiny(backwinding))
+               {
+                       FreeWinding (backwinding);
+                       backwinding = NULL;
+                       c_tinyportals++;
+               }
+
+               if (!frontwinding && !backwinding)
+               {       // tiny windings on both sides
+                       continue;
+               }
+
+               if (!frontwinding)
+               {
+                       FreeWinding (backwinding);
+                       if (side == 0)
+                               AddPortalToNodes (p, b, other_node);
+                       else
+                               AddPortalToNodes (p, other_node, b);
+                       continue;
+               }
+               if (!backwinding)
+               {
+                       FreeWinding (frontwinding);
+                       if (side == 0)
+                               AddPortalToNodes (p, f, other_node);
+                       else
+                               AddPortalToNodes (p, other_node, f);
+                       continue;
+               }
+               
+       // the winding is split
+               new_portal = AllocPortal ();
+               *new_portal = *p;
+               new_portal->winding = backwinding;
+               FreeWinding (p->winding);
+               p->winding = frontwinding;
+
+               if (side == 0)
+               {
+                       AddPortalToNodes (p, f, other_node);
+                       AddPortalToNodes (new_portal, b, other_node);
+               }
+               else
+               {
+                       AddPortalToNodes (p, other_node, f);
+                       AddPortalToNodes (new_portal, other_node, b);
+               }
+       }
+
+       node->portals = NULL;
+}
+
+
+/*
+================
+CalcNodeBounds
+================
+*/
+void CalcNodeBounds (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+       int                     i;
+
+       // calc mins/maxs for both leafs and nodes
+       ClearBounds (node->mins, node->maxs);
+       for (p = node->portals ; p ; p = p->next[s])    
+       {
+               s = (p->nodes[1] == node);
+               for (i=0 ; i<p->winding->numpoints ; i++)
+                       AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
+       }
+}
+
+
+/*
+==================
+MakeTreePortals_r
+==================
+*/
+void MakeTreePortals_r (node_t *node)
+{
+       int             i;
+
+       CalcNodeBounds (node);
+       if (node->mins[0] >= node->maxs[0])
+       {
+               Sys_Printf ("WARNING: node without a volume\n");
+       }
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (node->mins[i] < -8000 || node->maxs[i] > 8000)
+               {
+                       Sys_Printf ("WARNING: node with unbounded volume\n");
+                       break;
+               }
+       }
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       MakeNodePortal (node);
+       SplitNodePortals (node);
+
+       MakeTreePortals_r (node->children[0]);
+       MakeTreePortals_r (node->children[1]);
+}
+
+/*
+==================
+MakeTreePortals
+==================
+*/
+void MakeTreePortals (tree_t *tree)
+{
+       MakeHeadnodePortals (tree);
+       MakeTreePortals_r (tree->headnode);
+}
+
+/*
+=========================================================
+
+FLOOD ENTITIES
+
+=========================================================
+*/
+
+/*
+=============
+FloodPortals_r
+=============
+*/
+void FloodPortals_r (node_t *node, int dist)
+{
+       portal_t        *p;
+       int                     s;
+
+       node->occupied = dist;
+
+       for (p=node->portals ; p ; p = p->next[s])
+       {
+               s = (p->nodes[1] == node);
+
+               if (p->nodes[!s]->occupied)
+                       continue;
+
+               if (!Portal_EntityFlood (p, s))
+                       continue;
+
+               FloodPortals_r (p->nodes[!s], dist+1);
+       }
+}
+
+/*
+=============
+PlaceOccupant
+=============
+*/
+qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
+{
+       node_t  *node;
+       vec_t   d;
+       plane_t *plane;
+
+       // find the leaf to start in
+       node = headnode;
+       while (node->planenum != PLANENUM_LEAF)
+       {
+               plane = &mapplanes[node->planenum];
+               d = DotProduct (origin, plane->normal) - plane->dist;
+               if (d >= 0)
+                       node = node->children[0];
+               else
+                       node = node->children[1];
+       }
+
+       if (node->contents == CONTENTS_SOLID)
+               return false;
+       node->occupant = occupant;
+
+       FloodPortals_r (node, 1);
+
+       return true;
+}
+
+/*
+=============
+FloodEntities
+
+Marks all nodes that can be reached by entites
+=============
+*/
+qboolean FloodEntities (tree_t *tree)
+{
+       int             i;
+       vec3_t  origin;
+       char    *cl;
+       qboolean        inside;
+       node_t *headnode;
+
+       headnode = tree->headnode;
+       Sys_FPrintf( SYS_VRB, "--- FloodEntities ---\n");
+       inside = false;
+       tree->outside_node.occupied = 0;
+
+       for (i=1 ; i<num_entities ; i++)
+       {
+               GetVectorForKey (&entities[i], "origin", origin);
+               if (VectorCompare(origin, vec3_origin))
+                       continue;
+
+               cl = ValueForKey (&entities[i], "classname");
+               origin[2] += 1; // so objects on floor are ok
+
+               // nudge playerstart around if needed so clipping hulls allways
+               // have a vlaid point
+               if (!strcmp (cl, "info_player_start"))
+               {
+                       int     x, y;
+
+                       for (x=-16 ; x<=16 ; x += 16)
+                       {
+                               for (y=-16 ; y<=16 ; y += 16)
+                               {
+                                       origin[0] += x;
+                                       origin[1] += y;
+                                       if (PlaceOccupant (headnode, origin, &entities[i]))
+                                       {
+                                               inside = true;
+                                               goto gotit;
+                                       }
+                                       origin[0] -= x;
+                                       origin[1] -= y;
+                               }
+                       }
+gotit: ;
+               }
+               else
+               {
+                       if (PlaceOccupant (headnode, origin, &entities[i]))
+                               inside = true;
+               }
+       }
+
+       if (!inside)
+       {
+               Sys_FPrintf( SYS_VRB, "no entities in open -- no filling\n");
+       }
+       else if (tree->outside_node.occupied)
+       {
+               Sys_FPrintf( SYS_VRB, "entity reached from outside -- no filling\n");
+       }
+
+       return (qboolean)(inside && !tree->outside_node.occupied);
+}
+
+/*
+=========================================================
+
+FLOOD AREAS
+
+=========================================================
+*/
+
+int            c_areas;
+
+/*
+=============
+FloodAreas_r
+=============
+*/
+void FloodAreas_r (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+       bspbrush_t      *b;
+       entity_t        *e;
+
+       if (node->contents == CONTENTS_AREAPORTAL)
+       {
+               // this node is part of an area portal
+               b = node->brushlist;
+               e = &entities[b->original->entitynum];
+
+               // if the current area has allready touched this
+               // portal, we are done
+               if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
+                       return;
+
+               // note the current area as bounding the portal
+               if (e->portalareas[1])
+               {
+                       Sys_Printf ("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum);
+                       return;
+               }
+               if (e->portalareas[0])
+                       e->portalareas[1] = c_areas;
+               else
+                       e->portalareas[0] = c_areas;
+
+               return;
+       }
+
+       if (node->area)
+               return;         // allready got it
+       node->area = c_areas;
+
+       for (p=node->portals ; p ; p = p->next[s])
+       {
+               s = (p->nodes[1] == node);
+#if 0
+               if (p->nodes[!s]->occupied)
+                       continue;
+#endif
+               if (!Portal_EntityFlood (p, s))
+                       continue;
+
+               FloodAreas_r (p->nodes[!s]);
+       }
+}
+
+/*
+=============
+FindAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void FindAreas_r (node_t *node)
+{
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               FindAreas_r (node->children[0]);
+               FindAreas_r (node->children[1]);
+               return;
+       }
+
+       if (node->area)
+               return;         // allready got it
+
+       if (node->contents & CONTENTS_SOLID)
+               return;
+
+       if (!node->occupied)
+               return;                 // not reachable by entities
+
+       // area portals are allways only flooded into, never
+       // out of
+       if (node->contents == CONTENTS_AREAPORTAL)
+               return;
+
+       c_areas++;
+       FloodAreas_r (node);
+}
+
+/*
+=============
+SetAreaPortalAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void SetAreaPortalAreas_r (node_t *node)
+{
+       bspbrush_t      *b;
+       entity_t        *e;
+
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               SetAreaPortalAreas_r (node->children[0]);
+               SetAreaPortalAreas_r (node->children[1]);
+               return;
+       }
+
+       if (node->contents == CONTENTS_AREAPORTAL)
+       {
+               if (node->area)
+                       return;         // allready set
+
+               b = node->brushlist;
+               e = &entities[b->original->entitynum];
+               node->area = e->portalareas[0];
+               if (!e->portalareas[1])
+               {
+                       Sys_Printf ("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum);
+                       return;
+               }
+       }
+}
+
+/*
+=============
+EmitAreaPortals
+
+=============
+*/
+void EmitAreaPortals (node_t *headnode)
+{
+       int                             i, j;
+       entity_t                *e;
+       dareaportal_t   *dp;
+
+       if (c_areas > MAX_MAP_AREAS)
+               Error ("MAX_MAP_AREAS");
+       numareas = c_areas+1;
+       numareaportals = 1;             // leave 0 as an error
+
+       for (i=1 ; i<=c_areas ; i++)
+       {
+               dareas[i].firstareaportal = numareaportals;
+               for (j=0 ; j<num_entities ; j++)
+               {
+                       e = &entities[j];
+                       if (!e->areaportalnum)
+                               continue;
+                       dp = &dareaportals[numareaportals];
+                       if (e->portalareas[0] == i)
+                       {
+                               dp->portalnum = e->areaportalnum;
+                               dp->otherarea = e->portalareas[1];
+                               numareaportals++;
+                       }
+                       else if (e->portalareas[1] == i)
+                       {
+                               dp->portalnum = e->areaportalnum;
+                               dp->otherarea = e->portalareas[0];
+                               numareaportals++;
+                       }
+               }
+               dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;
+       }
+
+       Sys_FPrintf( SYS_VRB, "%5i numareas\n", numareas);
+       Sys_FPrintf( SYS_VRB, "%5i numareaportals\n", numareaportals);
+}
+
+/*
+=============
+FloodAreas
+
+Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
+=============
+*/
+void FloodAreas (tree_t *tree)
+{
+       Sys_FPrintf( SYS_VRB, "--- FloodAreas ---\n");
+       FindAreas_r (tree->headnode);
+       SetAreaPortalAreas_r (tree->headnode);
+       Sys_FPrintf( SYS_VRB, "%5i areas\n", c_areas);
+}
+
+//======================================================
+
+int            c_outside;
+int            c_inside;
+int            c_solid;
+
+void FillOutside_r (node_t *node)
+{
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               FillOutside_r (node->children[0]);
+               FillOutside_r (node->children[1]);
+               return;
+       }
+
+       // anything not reachable by an entity
+       // can be filled away
+       if (!node->occupied)
+       {
+               if (node->contents != CONTENTS_SOLID)
+               {
+                       c_outside++;
+                       node->contents = CONTENTS_SOLID;
+               }
+               else
+                       c_solid++;
+       }
+       else
+               c_inside++;
+
+}
+
+/*
+=============
+FillOutside
+
+Fill all nodes that can't be reached by entities
+=============
+*/
+void FillOutside (node_t *headnode)
+{
+       c_outside = 0;
+       c_inside = 0;
+       c_solid = 0;
+       Sys_FPrintf( SYS_VRB, "--- FillOutside ---\n");
+       FillOutside_r (headnode);
+       Sys_FPrintf( SYS_VRB, "%5i solid leafs\n", c_solid);
+       Sys_FPrintf( SYS_VRB, "%5i leafs filled\n", c_outside);
+       Sys_FPrintf( SYS_VRB, "%5i inside leafs\n", c_inside);
+}
+
+
+//==============================================================
+
+/*
+============
+FindPortalSide
+
+Finds a brush side to use for texturing the given portal
+============
+*/
+void FindPortalSide (portal_t *p)
+{
+       int                     viscontents;
+       bspbrush_t      *bb;
+       mapbrush_t      *brush;
+       node_t          *n;
+       int                     i,j;
+       int                     planenum;
+       side_t          *side, *bestside;
+       float           dot, bestdot;
+       plane_t         *p1, *p2;
+
+       // decide which content change is strongest
+       // solid > lava > water, etc
+       viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
+       if (!viscontents)
+               return;
+
+       planenum = p->onnode->planenum;
+       bestside = NULL;
+       bestdot = 0;
+
+       for (j=0 ; j<2 ; j++)
+       {
+               n = p->nodes[j];
+               p1 = &mapplanes[p->onnode->planenum];
+               for (bb=n->brushlist ; bb ; bb=bb->next)
+               {
+                       brush = bb->original;
+                       if ( !(brush->contents & viscontents) )
+                               continue;
+                       for (i=0 ; i<brush->numsides ; i++)
+                       {
+                               side = &brush->original_sides[i];
+                               if (side->bevel)
+                                       continue;
+                               if (side->texinfo == TEXINFO_NODE)
+                                       continue;               // non-visible
+                               if ((side->planenum&~1) == planenum)
+                               {       // exact match
+                                       bestside = &brush->original_sides[i];
+                                       goto gotit;
+                               }
+                               // see how close the match is
+                               p2 = &mapplanes[side->planenum&~1];
+                               dot = DotProduct (p1->normal, p2->normal);
+                               if (dot > bestdot)
+                               {
+                                       bestdot = dot;
+                                       bestside = side;
+                               }
+                       }
+               }
+       }
+
+gotit:
+       if (!bestside)
+               Sys_FPrintf( SYS_VRB, "WARNING: side not found for portal\n");
+
+       p->sidefound = true;
+       p->side = bestside;
+}
+
+
+/*
+===============
+MarkVisibleSides_r
+
+===============
+*/
+void MarkVisibleSides_r (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               MarkVisibleSides_r (node->children[0]);
+               MarkVisibleSides_r (node->children[1]);
+               return;
+       }
+
+       // empty leafs are never boundary leafs
+       if (!node->contents)
+               return;
+
+       // see if there is a visible face
+       for (p=node->portals ; p ; p = p->next[!s])
+       {
+               s = (p->nodes[0] == node);
+               if (!p->onnode)
+                       continue;               // edge of world
+               if (!p->sidefound)
+                       FindPortalSide (p);
+               if (p->side)
+                       p->side->visible = true;
+       }
+
+}
+
+/*
+=============
+MarkVisibleSides
+
+=============
+*/
+void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush)
+{
+       int             i, j;
+       mapbrush_t      *mb;
+       int             numsides;
+
+       Sys_FPrintf( SYS_VRB, "--- MarkVisibleSides ---\n");
+
+       // clear all the visible flags
+       for (i=startbrush ; i<endbrush ; i++)
+       {
+               mb = &mapbrushes[i];
+
+               numsides = mb->numsides;
+               for (j=0 ; j<numsides ; j++)
+                       mb->original_sides[j].visible = false;
+       }
+
+       // set visible flags on the sides that are used by portals
+       MarkVisibleSides_r (tree->headnode);
+}
+