Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

portals.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 
00023 #include "qbsp.h"
00024 
00025 
00026 int     c_active_portals;
00027 int     c_peak_portals;
00028 int     c_boundary;
00029 int     c_boundary_sides;
00030 
00031 /*
00032 ===========
00033 AllocPortal
00034 ===========
00035 */
00036 portal_t *AllocPortal (void)
00037 {
00038     portal_t    *p;
00039     
00040     if (numthreads == 1)
00041         c_active_portals++;
00042     if (c_active_portals > c_peak_portals)
00043         c_peak_portals = c_active_portals;
00044     
00045     p = malloc (sizeof(portal_t));
00046     memset (p, 0, sizeof(portal_t));
00047     
00048     return p;
00049 }
00050 
00051 void FreePortal (portal_t *p)
00052 {
00053     if (p->winding)
00054         FreeWinding (p->winding);
00055     if (numthreads == 1)
00056         c_active_portals--;
00057     free (p);
00058 }
00059 
00060 //==============================================================
00061 
00062 /*
00063 =============
00064 Portal_Passable
00065 
00066 Returns true if the portal has non-opaque leafs on both sides
00067 =============
00068 */
00069 qboolean Portal_Passable(portal_t *p) {
00070     if (!p->onnode) {
00071         return qfalse;  // to global outsideleaf
00072     }
00073 
00074     if (p->nodes[0]->planenum != PLANENUM_LEAF
00075         || p->nodes[1]->planenum != PLANENUM_LEAF) {
00076         Error ("Portal_EntityFlood: not a leaf");
00077     }
00078 
00079     if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
00080         return qtrue;
00081     }
00082 
00083     return qfalse;
00084 }
00085 
00086 
00087 //=============================================================================
00088 
00089 int     c_tinyportals;
00090 
00091 /*
00092 =============
00093 AddPortalToNodes
00094 =============
00095 */
00096 void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
00097 {
00098     if (p->nodes[0] || p->nodes[1])
00099         Error ("AddPortalToNode: allready included");
00100 
00101     p->nodes[0] = front;
00102     p->next[0] = front->portals;
00103     front->portals = p;
00104     
00105     p->nodes[1] = back;
00106     p->next[1] = back->portals;
00107     back->portals = p;
00108 }
00109 
00110 
00111 /*
00112 =============
00113 RemovePortalFromNode
00114 =============
00115 */
00116 void RemovePortalFromNode (portal_t *portal, node_t *l)
00117 {
00118     portal_t    **pp, *t;
00119     
00120 // remove reference to the current portal
00121     pp = &l->portals;
00122     while (1)
00123     {
00124         t = *pp;
00125         if (!t)
00126             Error ("RemovePortalFromNode: portal not in leaf"); 
00127 
00128         if ( t == portal )
00129             break;
00130 
00131         if (t->nodes[0] == l)
00132             pp = &t->next[0];
00133         else if (t->nodes[1] == l)
00134             pp = &t->next[1];
00135         else
00136             Error ("RemovePortalFromNode: portal not bounding leaf");
00137     }
00138     
00139     if (portal->nodes[0] == l)
00140     {
00141         *pp = portal->next[0];
00142         portal->nodes[0] = NULL;
00143     }
00144     else if (portal->nodes[1] == l)
00145     {
00146         *pp = portal->next[1];  
00147         portal->nodes[1] = NULL;
00148     }
00149 }
00150 
00151 //============================================================================
00152 
00153 void PrintPortal (portal_t *p)
00154 {
00155     int         i;
00156     winding_t   *w;
00157     
00158     w = p->winding;
00159     for (i=0 ; i<w->numpoints ; i++)
00160         _printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
00161         , w->p[i][1], w->p[i][2]);
00162 }
00163 
00164 /*
00165 ================
00166 MakeHeadnodePortals
00167 
00168 The created portals will face the global outside_node
00169 ================
00170 */
00171 #define SIDESPACE   8
00172 void MakeHeadnodePortals (tree_t *tree)
00173 {
00174     vec3_t      bounds[2];
00175     int         i, j, n;
00176     portal_t    *p, *portals[6];
00177     plane_t     bplanes[6], *pl;
00178     node_t *node;
00179 
00180     node = tree->headnode;
00181 
00182 // pad with some space so there will never be null volume leafs
00183     for (i=0 ; i<3 ; i++)
00184     {
00185         bounds[0][i] = tree->mins[i] - SIDESPACE;
00186         bounds[1][i] = tree->maxs[i] + SIDESPACE;
00187         if ( bounds[0][i] >= bounds[1][i] ) {
00188             Error( "Backwards tree volume" );
00189         }
00190     }
00191     
00192     tree->outside_node.planenum = PLANENUM_LEAF;
00193     tree->outside_node.brushlist = NULL;
00194     tree->outside_node.portals = NULL;
00195     tree->outside_node.opaque = qfalse;
00196 
00197     for (i=0 ; i<3 ; i++)
00198         for (j=0 ; j<2 ; j++)
00199         {
00200             n = j*3 + i;
00201 
00202             p = AllocPortal ();
00203             portals[n] = p;
00204             
00205             pl = &bplanes[n];
00206             memset (pl, 0, sizeof(*pl));
00207             if (j)
00208             {
00209                 pl->normal[i] = -1;
00210                 pl->dist = -bounds[j][i];
00211             }
00212             else
00213             {
00214                 pl->normal[i] = 1;
00215                 pl->dist = bounds[j][i];
00216             }
00217             p->plane = *pl;
00218             p->winding = BaseWindingForPlane (pl->normal, pl->dist);
00219             AddPortalToNodes (p, node, &tree->outside_node);
00220         }
00221         
00222 // clip the basewindings by all the other planes
00223     for (i=0 ; i<6 ; i++)
00224     {
00225         for (j=0 ; j<6 ; j++)
00226         {
00227             if (j == i)
00228                 continue;
00229             ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
00230         }
00231     }
00232 }
00233 
00234 //===================================================
00235 
00236 
00237 /*
00238 ================
00239 BaseWindingForNode
00240 ================
00241 */
00242 #define BASE_WINDING_EPSILON    0.001
00243 #define SPLIT_WINDING_EPSILON   0.001
00244 
00245 winding_t   *BaseWindingForNode (node_t *node)
00246 {
00247     winding_t   *w;
00248     node_t      *n;
00249     plane_t     *plane;
00250     vec3_t      normal;
00251     vec_t       dist;
00252 
00253     w = BaseWindingForPlane (mapplanes[node->planenum].normal
00254         , mapplanes[node->planenum].dist);
00255 
00256     // clip by all the parents
00257     for (n=node->parent ; n && w ; )
00258     {
00259         plane = &mapplanes[n->planenum];
00260 
00261         if (n->children[0] == node)
00262         {   // take front
00263             ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
00264         }
00265         else
00266         {   // take back
00267             VectorSubtract (vec3_origin, plane->normal, normal);
00268             dist = -plane->dist;
00269             ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
00270         }
00271         node = n;
00272         n = n->parent;
00273     }
00274 
00275     return w;
00276 }
00277 
00278 //============================================================
00279 
00280 /*
00281 ==================
00282 MakeNodePortal
00283 
00284 create the new portal by taking the full plane winding for the cutting plane
00285 and clipping it by all of parents of this node
00286 ==================
00287 */
00288 void MakeNodePortal (node_t *node)
00289 {
00290     portal_t    *new_portal, *p;
00291     winding_t   *w;
00292     vec3_t      normal;
00293     float       dist;
00294     int         side;
00295 
00296     w = BaseWindingForNode (node);
00297 
00298     // clip the portal by all the other portals in the node
00299     for (p = node->portals ; p && w; p = p->next[side]) 
00300     {
00301         if (p->nodes[0] == node)
00302         {
00303             side = 0;
00304             VectorCopy (p->plane.normal, normal);
00305             dist = p->plane.dist;
00306         }
00307         else if (p->nodes[1] == node)
00308         {
00309             side = 1;
00310             VectorSubtract (vec3_origin, p->plane.normal, normal);
00311             dist = -p->plane.dist;
00312         }
00313         else
00314             Error ("CutNodePortals_r: mislinked portal");
00315 
00316         ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON);
00317     }
00318 
00319     if (!w)
00320     {
00321         return;
00322     }
00323 
00324     if (WindingIsTiny (w))
00325     {
00326         c_tinyportals++;
00327         FreeWinding (w);
00328         return;
00329     }
00330 
00331     new_portal = AllocPortal ();
00332     new_portal->plane = mapplanes[node->planenum];
00333     new_portal->onnode = node;
00334     new_portal->winding = w;
00335     new_portal->hint = node->hint;
00336     AddPortalToNodes (new_portal, node->children[0], node->children[1]);
00337 }
00338 
00339 
00340 /*
00341 ==============
00342 SplitNodePortals
00343 
00344 Move or split the portals that bound node so that the node's
00345 children have portals instead of node.
00346 ==============
00347 */
00348 void SplitNodePortals (node_t *node)
00349 {
00350     portal_t    *p, *next_portal, *new_portal;
00351     node_t      *f, *b, *other_node;
00352     int         side;
00353     plane_t     *plane;
00354     winding_t   *frontwinding, *backwinding;
00355 
00356     plane = &mapplanes[node->planenum];
00357     f = node->children[0];
00358     b = node->children[1];
00359 
00360     for (p = node->portals ; p ; p = next_portal)   
00361     {
00362         if (p->nodes[0] == node)
00363             side = 0;
00364         else if (p->nodes[1] == node)
00365             side = 1;
00366         else
00367             Error ("SplitNodePortals: mislinked portal");
00368         next_portal = p->next[side];
00369 
00370         other_node = p->nodes[!side];
00371         RemovePortalFromNode (p, p->nodes[0]);
00372         RemovePortalFromNode (p, p->nodes[1]);
00373 
00374 //
00375 // cut the portal into two portals, one on each side of the cut plane
00376 //
00377         ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
00378             SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
00379 
00380         if (frontwinding && WindingIsTiny(frontwinding))
00381         {
00382             if (!f->tinyportals)
00383                 VectorCopy(frontwinding->p[0], f->referencepoint);
00384             f->tinyportals++;
00385             if (!other_node->tinyportals)
00386                 VectorCopy(frontwinding->p[0], other_node->referencepoint);
00387             other_node->tinyportals++;
00388 
00389             FreeWinding (frontwinding);
00390             frontwinding = NULL;
00391             c_tinyportals++;
00392         }
00393 
00394         if (backwinding && WindingIsTiny(backwinding))
00395         {
00396             if (!b->tinyportals)
00397                 VectorCopy(backwinding->p[0], b->referencepoint);
00398             b->tinyportals++;
00399             if (!other_node->tinyportals)
00400                 VectorCopy(backwinding->p[0], other_node->referencepoint);
00401             other_node->tinyportals++;
00402 
00403             FreeWinding (backwinding);
00404             backwinding = NULL;
00405             c_tinyportals++;
00406         }
00407 
00408         if (!frontwinding && !backwinding)
00409         {   // tiny windings on both sides
00410             continue;
00411         }
00412 
00413         if (!frontwinding)
00414         {
00415             FreeWinding (backwinding);
00416             if (side == 0)
00417                 AddPortalToNodes (p, b, other_node);
00418             else
00419                 AddPortalToNodes (p, other_node, b);
00420             continue;
00421         }
00422         if (!backwinding)
00423         {
00424             FreeWinding (frontwinding);
00425             if (side == 0)
00426                 AddPortalToNodes (p, f, other_node);
00427             else
00428                 AddPortalToNodes (p, other_node, f);
00429             continue;
00430         }
00431         
00432     // the winding is split
00433         new_portal = AllocPortal ();
00434         *new_portal = *p;
00435         new_portal->winding = backwinding;
00436         FreeWinding (p->winding);
00437         p->winding = frontwinding;
00438 
00439         if (side == 0)
00440         {
00441             AddPortalToNodes (p, f, other_node);
00442             AddPortalToNodes (new_portal, b, other_node);
00443         }
00444         else
00445         {
00446             AddPortalToNodes (p, other_node, f);
00447             AddPortalToNodes (new_portal, other_node, b);
00448         }
00449     }
00450 
00451     node->portals = NULL;
00452 }
00453 
00454 
00455 /*
00456 ================
00457 CalcNodeBounds
00458 ================
00459 */
00460 void CalcNodeBounds (node_t *node)
00461 {
00462     portal_t    *p;
00463     int         s;
00464     int         i;
00465 
00466     // calc mins/maxs for both leafs and nodes
00467     ClearBounds (node->mins, node->maxs);
00468     for (p = node->portals ; p ; p = p->next[s])
00469     {
00470         s = (p->nodes[1] == node);
00471         for (i=0 ; i<p->winding->numpoints ; i++)
00472             AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
00473     }
00474 }
00475 
00476 /*
00477 ==================
00478 MakeTreePortals_r
00479 ==================
00480 */
00481 void MakeTreePortals_r (node_t *node)
00482 {
00483     int     i;
00484 
00485     CalcNodeBounds (node);
00486     if (node->mins[0] >= node->maxs[0])
00487     {
00488         _printf ("WARNING: node without a volume\n");
00489         _printf("node has %d tiny portals\n", node->tinyportals);
00490         _printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0],
00491                                                             node->referencepoint[1],
00492                                                             node->referencepoint[2]);
00493     }
00494 
00495     for (i=0 ; i<3 ; i++)
00496     {
00497         if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD)
00498         {
00499             _printf ("WARNING: node with unbounded volume\n");
00500             break;
00501         }
00502     }
00503     if (node->planenum == PLANENUM_LEAF)
00504         return;
00505 
00506     MakeNodePortal (node);
00507     SplitNodePortals (node);
00508 
00509     MakeTreePortals_r (node->children[0]);
00510     MakeTreePortals_r (node->children[1]);
00511 }
00512 
00513 /*
00514 ==================
00515 MakeTreePortals
00516 ==================
00517 */
00518 void MakeTreePortals (tree_t *tree)
00519 {
00520     qprintf( "----- MakeTreePortals -----\n");
00521     MakeHeadnodePortals (tree);
00522     MakeTreePortals_r (tree->headnode);
00523     qprintf("%6d tiny portals\n", c_tinyportals);
00524 }
00525 
00526 /*
00527 =========================================================
00528 
00529 FLOOD ENTITIES
00530 
00531 =========================================================
00532 */
00533 
00534 int     c_floodedleafs;
00535 
00536 /*
00537 =============
00538 FloodPortals_r
00539 =============
00540 */
00541 void FloodPortals_r (node_t *node, int dist) {
00542     portal_t    *p;
00543     int         s;
00544 
00545     if ( node->occupied ) {
00546         return;
00547     }
00548 
00549     if ( node->opaque ) {
00550         return;
00551     }
00552 
00553     c_floodedleafs++;
00554     node->occupied = dist;
00555 
00556     for (p=node->portals ; p ; p = p->next[s]) {
00557         s = (p->nodes[1] == node);
00558         FloodPortals_r (p->nodes[!s], dist+1);
00559     }
00560 }
00561 
00562 /*
00563 =============
00564 PlaceOccupant
00565 =============
00566 */
00567 qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
00568 {
00569     node_t  *node;
00570     vec_t   d;
00571     plane_t *plane;
00572 
00573     // find the leaf to start in
00574     node = headnode;
00575     while (node->planenum != PLANENUM_LEAF)
00576     {
00577         plane = &mapplanes[node->planenum];
00578         d = DotProduct (origin, plane->normal) - plane->dist;
00579         if (d >= 0)
00580             node = node->children[0];
00581         else
00582             node = node->children[1];
00583     }
00584 
00585     if ( node->opaque )
00586         return qfalse;
00587     node->occupant = occupant;
00588 
00589     FloodPortals_r (node, 1);
00590 
00591     return qtrue;
00592 }
00593 
00594 /*
00595 =============
00596 FloodEntities
00597 
00598 Marks all nodes that can be reached by entites
00599 =============
00600 */
00601 qboolean FloodEntities( tree_t *tree ) {
00602     int     i;
00603     vec3_t  origin;
00604     const char  *cl;
00605     qboolean    inside;
00606     node_t *headnode;
00607 
00608     headnode = tree->headnode;
00609     qprintf ("--- FloodEntities ---\n");
00610     inside = qfalse;
00611     tree->outside_node.occupied = 0;
00612 
00613     c_floodedleafs = 0;
00614     for (i=1 ; i<num_entities ; i++)
00615     {
00616         GetVectorForKey (&entities[i], "origin", origin);
00617         if (VectorCompare(origin, vec3_origin))
00618             continue;
00619 
00620         cl = ValueForKey (&entities[i], "classname");
00621 
00622         origin[2] += 1; // so objects on floor are ok
00623 
00624         if (PlaceOccupant (headnode, origin, &entities[i]))
00625             inside = qtrue;
00626     }
00627 
00628     qprintf("%5i flooded leafs\n", c_floodedleafs );
00629 
00630     if (!inside)
00631     {
00632         qprintf ("no entities in open -- no filling\n");
00633     }
00634     else if (tree->outside_node.occupied)
00635     {
00636         qprintf ("entity reached from outside -- no filling\n");
00637     }
00638 
00639     return (qboolean)(inside && !tree->outside_node.occupied);
00640 }
00641 
00642 /*
00643 =========================================================
00644 
00645 FLOOD AREAS
00646 
00647 =========================================================
00648 */
00649 
00650 int     c_areas;
00651 
00652 /*
00653 =============
00654 FloodAreas_r
00655 =============
00656 */
00657 void FloodAreas_r (node_t *node)
00658 {
00659     portal_t    *p;
00660     int         s;
00661     bspbrush_t  *b;
00662 
00663     if ( node->areaportal ) {
00664         //
00665         if ( node->area == -1 ) {
00666             node->area = c_areas;
00667         }
00668         // this node is part of an area portal brush
00669         b = node->brushlist->original;
00670 
00671         // if the current area has allready touched this
00672         // portal, we are done
00673         if (b->portalareas[0] == c_areas || b->portalareas[1] == c_areas)
00674             return;
00675 
00676         // note the current area as bounding the portal
00677         if (b->portalareas[1] != -1)
00678         {
00679             _printf ("WARNING: areaportal brush %i touches > 2 areas\n", b->brushnum );
00680             return;
00681         }
00682         if (b->portalareas[0] != -1) {
00683             b->portalareas[1] = c_areas;
00684         } else {
00685             b->portalareas[0] = c_areas;
00686         }
00687 
00688         return;
00689     }
00690 
00691     if (node->area != -1) {
00692         return;     // allready got it
00693     }
00694     if ( node->cluster == -1 ) {
00695         return;
00696     }
00697 
00698     node->area = c_areas;
00699 
00700     for (p=node->portals ; p ; p = p->next[s])
00701     {
00702         s = (p->nodes[1] == node);
00703 
00704         if ( !Portal_Passable(p) )
00705             continue;
00706 
00707         FloodAreas_r (p->nodes[!s]);
00708     }
00709 }
00710 
00711 
00712 /*
00713 =============
00714 FindAreas_r
00715 
00716 Just decend the tree, and for each node that hasn't had an
00717 area set, flood fill out from there
00718 =============
00719 */
00720 void FindAreas_r (node_t *node)
00721 {
00722     if (node->planenum != PLANENUM_LEAF)
00723     {
00724         FindAreas_r (node->children[0]);
00725         FindAreas_r (node->children[1]);
00726         return;
00727     }
00728 
00729     if (node->opaque)
00730         return;
00731 
00732     if (node->areaportal)
00733         return;
00734 
00735     if (node->area != -1)
00736         return;     // allready got it
00737 
00738     FloodAreas_r (node);
00739     c_areas++;
00740 }
00741 
00742 /*
00743 =============
00744 CheckAreas_r
00745 =============
00746 */
00747 void CheckAreas_r (node_t *node)
00748 {
00749     bspbrush_t  *b;
00750 
00751     if (node->planenum != PLANENUM_LEAF)
00752     {
00753         CheckAreas_r (node->children[0]);
00754         CheckAreas_r (node->children[1]);
00755         return;
00756     }
00757 
00758     if (node->opaque)
00759         return;
00760 
00761     if (node->cluster != -1)
00762         if (node->area == -1)
00763             _printf("WARNING: cluster %d has area set to -1\n", node->cluster);
00764     if (node->areaportal)
00765     {
00766         b = node->brushlist->original;
00767 
00768         // check if the areaportal touches two areas
00769         if (b->portalareas[0] == -1 || b->portalareas[1] == -1)
00770             _printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushnum);
00771     }
00772 }
00773 
00774 /*
00775 =============
00776 FloodAreas
00777 
00778 Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
00779 =============
00780 */
00781 void FloodAreas (tree_t *tree)
00782 {
00783     qprintf ("--- FloodAreas ---\n");
00784     FindAreas_r( tree->headnode );
00785 
00786     // check for areaportal brushes that don't touch two areas
00787     CheckAreas_r( tree->headnode );
00788 
00789     qprintf ("%5i areas\n", c_areas);
00790 }
00791 
00792 //======================================================
00793 
00794 int     c_outside;
00795 int     c_inside;
00796 int     c_solid;
00797 
00798 void FillOutside_r (node_t *node)
00799 {
00800     if (node->planenum != PLANENUM_LEAF)
00801     {
00802         FillOutside_r (node->children[0]);
00803         FillOutside_r (node->children[1]);
00804         return;
00805     }
00806 
00807     // anything not reachable by an entity
00808     // can be filled away
00809     if (!node->occupied) {
00810         if ( !node->opaque ) {
00811             c_outside++;
00812             node->opaque = qtrue;
00813         } else {
00814             c_solid++;
00815         }
00816     } else {
00817         c_inside++;
00818     }
00819 
00820 }
00821 
00822 /*
00823 =============
00824 FillOutside
00825 
00826 Fill all nodes that can't be reached by entities
00827 =============
00828 */
00829 void FillOutside (node_t *headnode)
00830 {
00831     c_outside = 0;
00832     c_inside = 0;
00833     c_solid = 0;
00834     qprintf ("--- FillOutside ---\n");
00835     FillOutside_r (headnode);
00836     qprintf ("%5i solid leafs\n", c_solid);
00837     qprintf ("%5i leafs filled\n", c_outside);
00838     qprintf ("%5i inside leafs\n", c_inside);
00839 }
00840 
00841 
00842 //==============================================================
00843 

Generated on Thu Aug 25 12:37:19 2005 for Quake III Arena by  doxygen 1.3.9.1