-/*\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);
+}
+