portals.c

Go to the documentation of this file.
00001 
00008 /*
00009 Copyright (C) 1997-2001 Id Software, Inc.
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020 See the GNU General Public License for more details.
00021 
00022 You should have received a copy of the GNU General Public License
00023 along with this program; if not, write to the Free Software
00024 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00025 
00026 */
00027 
00028 
00029 #include "bsp.h"
00030 
00031 
00032 static int c_active_portals = 0;
00033 static int c_peak_portals = 0;
00034 static int c_tinyportals = 0;
00035 
00039 static portal_t *AllocPortal (void)
00040 {
00041     if (threadstate.numthreads == 1)
00042         c_active_portals++;
00043     if (c_active_portals > c_peak_portals)
00044         c_peak_portals = c_active_portals;
00045 
00046     return Mem_Alloc(sizeof(portal_t));
00047 }
00048 
00052 void FreePortal (portal_t *p)
00053 {
00054     if (p->winding)
00055         FreeWinding(p->winding);
00056     if (threadstate.numthreads == 1)
00057         c_active_portals--;
00058     Mem_Free(p);
00059 }
00060 
00064 int VisibleContents (int contentFlags)
00065 {
00066     int i;
00067 
00068     for (i = 1; i <= LAST_VISIBLE_CONTENTS; i <<= 1)
00069         if (contentFlags & i)
00070             return i;
00071 
00072     return 0;
00073 }
00074 
00082 static void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
00083 {
00084     if (p->nodes[0] || p->nodes[1])
00085         Sys_Error("AddPortalToNodes: already included");
00086 
00087     p->nodes[0] = front;
00088     p->next[0] = front->portals;
00089     front->portals = p;
00090 
00091     p->nodes[1] = back;
00092     p->next[1] = back->portals;
00093     back->portals = p;
00094 }
00095 
00102 void RemovePortalFromNode (portal_t *portal, node_t *l)
00103 {
00104     portal_t **pp;
00105 
00106     /* remove reference to the current portal */
00107     pp = &l->portals;
00108     while (1) {
00109         portal_t *t = *pp;
00110         if (!t)
00111             Sys_Error("RemovePortalFromNode: portal not in leaf");
00112 
00113         if (t == portal)
00114             break;
00115 
00116         if (t->nodes[0] == l)
00117             pp = &t->next[0];
00118         else if (t->nodes[1] == l)
00119             pp = &t->next[1];
00120         else
00121             Sys_Error("RemovePortalFromNode: portal not bounding leaf");
00122     }
00123 
00124     if (portal->nodes[0] == l) {
00125         *pp = portal->next[0];
00126         portal->nodes[0] = NULL;
00127     } else if (portal->nodes[1] == l) {
00128         *pp = portal->next[1];
00129         portal->nodes[1] = NULL;
00130     }
00131 }
00132 
00133 #define SIDESPACE   8
00134 
00137 static void MakeHeadnodePortals (tree_t *tree)
00138 {
00139     vec3_t bounds[2];
00140     int i, j;
00141     portal_t *portals[6];
00142     plane_t bplanes[6];
00143     node_t *node;
00144 
00145     node = tree->headnode;
00146 
00147     /* pad with some space so there will never be null volume leafs */
00148     for (i = 0; i < 3; i++) {
00149         bounds[0][i] = tree->mins[i] - SIDESPACE;
00150         bounds[1][i] = tree->maxs[i] + SIDESPACE;
00151     }
00152 
00153     tree->outside_node.planenum = PLANENUM_LEAF;
00154     tree->outside_node.brushlist = NULL;
00155     tree->outside_node.portals = NULL;
00156     tree->outside_node.contentFlags = 0;
00157 
00158     for (i = 0; i < 3; i++)
00159         for (j = 0; j < 2; j++) {
00160             const int n = j * 3 + i;
00161             portal_t *p = AllocPortal();
00162             plane_t *pl = &bplanes[n];
00163 
00164             memset(pl, 0, sizeof(*pl));
00165             portals[n] = p;
00166 
00167             if (j) {
00168                 pl->normal[i] = -1;
00169                 pl->dist = -bounds[j][i];
00170             } else {
00171                 pl->normal[i] = 1;
00172                 pl->dist = bounds[j][i];
00173             }
00174             p->plane = *pl;
00175             p->winding = BaseWindingForPlane(pl->normal, pl->dist);
00176             AddPortalToNodes(p, node, &tree->outside_node);
00177         }
00178 
00179     /* clip the basewindings by all the other planes */
00180     for (i = 0; i < 6; i++) {
00181         for (j = 0; j < 6; j++) {
00182             if (j == i)
00183                 continue;
00184             ChopWindingInPlace(&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
00185         }
00186     }
00187 }
00188 
00189 #define BASE_WINDING_EPSILON    0.001
00190 #define SPLIT_WINDING_EPSILON   0.001
00191 
00192 static winding_t *BaseWindingForNode (node_t *node)
00193 {
00194     node_t *n;
00195     winding_t *w;
00196 
00197     w = BaseWindingForPlane(mapplanes[node->planenum].normal
00198         , mapplanes[node->planenum].dist);
00199 
00200     /* clip by all the parents */
00201     for (n = node->parent; n && w;) {
00202         const plane_t *plane = &mapplanes[n->planenum];
00203 
00204         if (n->children[0] == node) {   /* take front */
00205             ChopWindingInPlace(&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
00206         } else {    /* take back */
00207             const vec_t dist = -plane->dist;
00208             vec3_t normal;
00209             VectorSubtract(vec3_origin, plane->normal, normal);
00210             ChopWindingInPlace(&w, normal, dist, BASE_WINDING_EPSILON);
00211         }
00212         node = n;
00213         n = n->parent;
00214     }
00215 
00216     return w;
00217 }
00218 
00219 /*============================================================ */
00220 
00225 static void MakeNodePortal (node_t *node)
00226 {
00227     portal_t *new_portal, *p;
00228     winding_t *w;
00229     vec3_t normal;
00230     float dist = 0.0f;
00231     int side = 0;
00232 
00233     w = BaseWindingForNode(node);
00234 
00235     /* clip the portal by all the other portals in the node */
00236     for (p = node->portals; p && w; p = p->next[side]) {
00237         if (p->nodes[0] == node) {
00238             side = 0;
00239             VectorCopy(p->plane.normal, normal);
00240             dist = p->plane.dist;
00241         } else if (p->nodes[1] == node) {
00242             side = 1;
00243             VectorSubtract(vec3_origin, p->plane.normal, normal);
00244             dist = -p->plane.dist;
00245         } else
00246             Sys_Error("CutNodePortals_r: mislinked portal");
00247 
00248         ChopWindingInPlace(&w, normal, dist, 0.1);
00249     }
00250 
00251     if (!w)
00252         return;
00253 
00254     if (WindingIsTiny(w)) {
00255         c_tinyportals++;
00256         FreeWinding(w);
00257         return;
00258     }
00259 
00260     new_portal = AllocPortal();
00261     new_portal->plane = mapplanes[node->planenum];
00262     new_portal->onnode = node;
00263     new_portal->winding = w;
00264     AddPortalToNodes(new_portal, node->children[0], node->children[1]);
00265 }
00266 
00267 
00272 static void SplitNodePortals (node_t *node)
00273 {
00274     portal_t *p, *next_portal, *new_portal;
00275     node_t *f, *b, *other_node;
00276     int side = 0;
00277     plane_t *plane;
00278     winding_t *frontwinding, *backwinding;
00279 
00280     plane = &mapplanes[node->planenum];
00281     f = node->children[0];
00282     b = node->children[1];
00283 
00284     for (p = node->portals; p; p = next_portal) {
00285         if (p->nodes[0] == node)
00286             side = 0;
00287         else if (p->nodes[1] == node)
00288             side = 1;
00289         else
00290             Sys_Error("CutNodePortals_r: mislinked portal");
00291         next_portal = p->next[side];
00292 
00293         other_node = p->nodes[!side];
00294         RemovePortalFromNode(p, p->nodes[0]);
00295         RemovePortalFromNode(p, p->nodes[1]);
00296 
00297         /* cut the portal into two portals, one on each side of the cut plane */
00298         ClipWindingEpsilon(p->winding, plane->normal, plane->dist,
00299             SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
00300 
00301         if (frontwinding && WindingIsTiny(frontwinding)) {
00302             FreeWinding(frontwinding);
00303             frontwinding = NULL;
00304             c_tinyportals++;
00305         }
00306 
00307         if (backwinding && WindingIsTiny(backwinding)) {
00308             FreeWinding(backwinding);
00309             backwinding = NULL;
00310             c_tinyportals++;
00311         }
00312 
00313         if (!frontwinding && !backwinding) {    /* tiny windings on both sides */
00314             continue;
00315         }
00316 
00317         if (!frontwinding) {
00318             FreeWinding(backwinding);
00319             if (side == 0)
00320                 AddPortalToNodes(p, b, other_node);
00321             else
00322                 AddPortalToNodes(p, other_node, b);
00323             continue;
00324         }
00325         if (!backwinding) {
00326             FreeWinding(frontwinding);
00327             if (side == 0)
00328                 AddPortalToNodes(p, f, other_node);
00329             else
00330                 AddPortalToNodes(p, other_node, f);
00331             continue;
00332         }
00333 
00334         /* the winding is split */
00335         new_portal = AllocPortal();
00336         *new_portal = *p;
00337         new_portal->winding = backwinding;
00338         FreeWinding(p->winding);
00339         p->winding = frontwinding;
00340 
00341         if (side == 0) {
00342             AddPortalToNodes(p, f, other_node);
00343             AddPortalToNodes(new_portal, b, other_node);
00344         } else {
00345             AddPortalToNodes(p, other_node, f);
00346             AddPortalToNodes(new_portal, other_node, b);
00347         }
00348     }
00349 
00350     node->portals = NULL;
00351 }
00352 
00353 
00354 static void CalcNodeBounds (node_t *node)
00355 {
00356     portal_t *p;
00357     int s, i;
00358 
00359     /* calc mins/maxs for both leafs and nodes */
00360     ClearBounds(node->mins, node->maxs);
00361     for (p = node->portals; p; p = p->next[s]) {
00362         s = (p->nodes[1] == node);
00363         if (!p->winding)
00364             continue;
00365         for (i = 0; i < p->winding->numpoints; i++)
00366             AddPointToBounds(p->winding->p[i], node->mins, node->maxs);
00367     }
00368 }
00369 
00370 
00371 static void MakeTreePortals_r (node_t *node)
00372 {
00373     int i;
00374 
00375     CalcNodeBounds(node);
00376     if (node->mins[0] >= node->maxs[0]) {
00377         Com_Printf("WARNING: node without a volume\n");
00378     }
00379 
00380     for (i = 0; i < 3; i++) {
00381         if (node->mins[i] < -MAX_WORLD_WIDTH || node->maxs[i] > MAX_WORLD_WIDTH) {
00382             Com_Printf("WARNING: node with unbounded volume %i\n", (int)node->mins[i]);
00383             break;
00384         }
00385     }
00386     if (node->planenum == PLANENUM_LEAF)
00387         return;
00388 
00389     MakeNodePortal(node);
00390     SplitNodePortals(node);
00391 
00392     MakeTreePortals_r(node->children[0]);
00393     MakeTreePortals_r(node->children[1]);
00394 }
00395 
00396 void MakeTreePortals (tree_t *tree)
00397 {
00398     MakeHeadnodePortals(tree);
00399     MakeTreePortals_r(tree->headnode);
00400 }
00401 
00405 static void FindPortalSide (portal_t *p)
00406 {
00407     int viscontents;
00408     bspbrush_t *bb;
00409     int i, j, planenum;
00410     side_t *bestside;
00411     float bestdot;
00412 
00413     /* decide which content change is strongest
00414      * solid > water, etc */
00415     viscontents = VisibleContents(p->nodes[0]->contentFlags ^ p->nodes[1]->contentFlags);
00416     if (!viscontents)
00417         return;
00418 
00419     planenum = p->onnode->planenum;
00420     bestside = NULL;
00421     bestdot = 0;
00422 
00423     for (j = 0; j < 2; j++) {
00424         const node_t *n = p->nodes[j];
00425         const plane_t *p1 = &mapplanes[p->onnode->planenum];
00426         for (bb = n->brushlist; bb; bb = bb->next) {
00427             const mapbrush_t *brush = bb->original;
00428 
00429             if (!(brush->contentFlags & viscontents))
00430                 continue;
00431             for (i = 0; i < brush->numsides; i++) {
00432                 side_t *side = &brush->original_sides[i];
00433                 float dot;
00434                 plane_t *p2;
00435 
00436                 if (side->bevel)
00437                     continue;
00438                 if (side->texinfo == TEXINFO_NODE)
00439                     continue;       /* non-visible */
00440                 if ((side->planenum &~ 1) == planenum) {    /* exact match */
00441                     bestside = &brush->original_sides[i];
00442                     goto gotit;
00443                 }
00444                 /* see how close the match is */
00445                 p2 = &mapplanes[side->planenum &~ 1];
00446                 dot = DotProduct(p1->normal, p2->normal);
00447                 if (dot > bestdot) {
00448                     bestdot = dot;
00449                     bestside = side;
00450                 }
00451             }
00452         }
00453     }
00454 
00455 gotit:
00456     if (!bestside)
00457         Verb_Printf(VERB_EXTRA, "WARNING: side not found for portal\n");
00458 
00459     p->sidefound = qtrue;
00460     p->side = bestside;
00461 }
00462 
00463 
00464 static void MarkVisibleSides_r (node_t *node)
00465 {
00466     portal_t *p;
00467     int s;
00468 
00469     if (node->planenum != PLANENUM_LEAF) {
00470         MarkVisibleSides_r(node->children[0]);
00471         MarkVisibleSides_r(node->children[1]);
00472         return;
00473     }
00474 
00475     /* empty leafs are never boundary leafs */
00476     if (!node->contentFlags)
00477         return;
00478 
00479     /* see if there is a visible face */
00480     for (p = node->portals; p; p = p->next[!s]) {
00481         s = (p->nodes[0] == node);
00482         if (!p->onnode)
00483             continue;       /* edge of world */
00484         if (!p->sidefound)
00485             FindPortalSide(p);
00486         if (p->side)
00487             p->side->visible = qtrue;
00488     }
00489 }
00490 
00491 void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush)
00492 {
00493     int i;
00494 
00495     Verb_Printf(VERB_EXTRA, "--- MarkVisibleSides ---\n");
00496 
00497     /* clear all the visible flags */
00498     for (i = startbrush; i < endbrush; i++) {
00499         mapbrush_t *mb = &mapbrushes[i];
00500         const int numsides = mb->numsides;
00501         int j;
00502 
00503         for (j = 0; j < numsides; j++)
00504             mb->original_sides[j].visible = qfalse;
00505     }
00506 
00507     /* set visible flags on the sides that are used by portals */
00508     MarkVisibleSides_r(tree->headnode);
00509 }

Generated by  doxygen 1.6.2