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