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 #include "l_mem.h"
00025 
00026 int     c_active_portals;
00027 int     c_peak_portals;
00028 int     c_boundary;
00029 int     c_boundary_sides;
00030 int     c_portalmemory;
00031 
00032 //portal_t *portallist = NULL;
00033 //===========================================================================
00034 //
00035 // Parameter:               -
00036 // Returns:                 -
00037 // Changes Globals:     -
00038 //===========================================================================
00039 portal_t *AllocPortal (void)
00040 {
00041     portal_t    *p;
00042     
00043     p = GetMemory(sizeof(portal_t));
00044     memset (p, 0, sizeof(portal_t));
00045 
00046     if (numthreads == 1)
00047     {
00048         c_active_portals++;
00049         if (c_active_portals > c_peak_portals)
00050         {
00051             c_peak_portals = c_active_portals;
00052         } //end if
00053         c_portalmemory += MemorySize(p);    
00054     } //end if
00055 
00056 //  p->nextportal = portallist;
00057 //  portallist = p;
00058     
00059     return p;
00060 } //end of the function AllocPortal
00061 //===========================================================================
00062 //
00063 // Parameter:               -
00064 // Returns:                 -
00065 // Changes Globals:     -
00066 //===========================================================================
00067 void FreePortal (portal_t *p)
00068 {
00069     if (p->winding) FreeWinding(p->winding);
00070     if (numthreads == 1)
00071     {
00072         c_active_portals--;
00073         c_portalmemory -= MemorySize(p);
00074     } //end if
00075     FreeMemory(p);
00076 } //end of the function FreePortal
00077 //===========================================================================
00078 // Returns the single content bit of the
00079 // strongest visible content present
00080 //
00081 // Parameter:               -
00082 // Returns:                 -
00083 // Changes Globals:     -
00084 //===========================================================================
00085 int VisibleContents (int contents)
00086 {
00087     int     i;
00088 
00089     for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
00090         if (contents & i )
00091             return i;
00092 
00093     return 0;
00094 } //end of the function VisibleContents
00095 //===========================================================================
00096 //
00097 // Parameter:               -
00098 // Returns:                 -
00099 // Changes Globals:     -
00100 //===========================================================================
00101 int ClusterContents (node_t *node)
00102 {
00103     int     c1, c2, c;
00104 
00105     if (node->planenum == PLANENUM_LEAF)
00106         return node->contents;
00107 
00108     c1 = ClusterContents(node->children[0]);
00109     c2 = ClusterContents(node->children[1]);
00110     c = c1|c2;
00111 
00112     // a cluster may include some solid detail areas, but
00113     // still be seen into
00114     if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
00115         c &= ~CONTENTS_SOLID;
00116     return c;
00117 } //end of the function ClusterContents
00118 
00119 //===========================================================================
00120 // Returns true if the portal is empty or translucent, allowing
00121 // the PVS calculation to see through it.
00122 // The nodes on either side of the portal may actually be clusters,
00123 // not leaves, so all contents should be ored together
00124 //
00125 // Parameter:               -
00126 // Returns:                 -
00127 // Changes Globals:     -
00128 //===========================================================================
00129 qboolean Portal_VisFlood (portal_t *p)
00130 {
00131     int     c1, c2;
00132 
00133     if (!p->onnode)
00134         return false;   // to global outsideleaf
00135 
00136     c1 = ClusterContents(p->nodes[0]);
00137     c2 = ClusterContents(p->nodes[1]);
00138 
00139     if (!VisibleContents (c1^c2))
00140         return true;
00141 
00142     if (c1 & (CONTENTS_Q2TRANSLUCENT|CONTENTS_DETAIL))
00143         c1 = 0;
00144     if (c2 & (CONTENTS_Q2TRANSLUCENT|CONTENTS_DETAIL))
00145         c2 = 0;
00146 
00147     if ( (c1|c2) & CONTENTS_SOLID )
00148         return false;       // can't see through solid
00149 
00150     if (! (c1 ^ c2))
00151         return true;        // identical on both sides
00152 
00153     if (!VisibleContents (c1^c2))
00154         return true;
00155     return false;
00156 } //end of the function Portal_VisFlood
00157 //===========================================================================
00158 // The entity flood determines which areas are
00159 // "outside" on the map, which are then filled in.
00160 // Flowing from side s to side !s
00161 //
00162 // Parameter:               -
00163 // Returns:                 -
00164 // Changes Globals:     -
00165 //===========================================================================
00166 qboolean Portal_EntityFlood (portal_t *p, int s)
00167 {
00168     if (p->nodes[0]->planenum != PLANENUM_LEAF
00169         || p->nodes[1]->planenum != PLANENUM_LEAF)
00170         Error ("Portal_EntityFlood: not a leaf");
00171 
00172     // can never cross to a solid 
00173     if ( (p->nodes[0]->contents & CONTENTS_SOLID)
00174     || (p->nodes[1]->contents & CONTENTS_SOLID) )
00175         return false;
00176 
00177     // can flood through everything else
00178     return true;
00179 }
00180 
00181 
00182 //=============================================================================
00183 
00184 int     c_tinyportals;
00185 
00186 //===========================================================================
00187 //
00188 // Parameter:               -
00189 // Returns:                 -
00190 // Changes Globals:     -
00191 //===========================================================================
00192 void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
00193 {
00194     if (p->nodes[0] || p->nodes[1])
00195         Error ("AddPortalToNode: allready included");
00196 
00197     p->nodes[0] = front;
00198     p->next[0] = front->portals;
00199     front->portals = p;
00200     
00201     p->nodes[1] = back;
00202     p->next[1] = back->portals;
00203     back->portals = p;
00204 } //end of the function AddPortalToNodes
00205 //===========================================================================
00206 //
00207 // Parameter:               -
00208 // Returns:                 -
00209 // Changes Globals:     -
00210 //===========================================================================
00211 void RemovePortalFromNode (portal_t *portal, node_t *l)
00212 {
00213     portal_t    **pp, *t;
00214 
00215     int s, i, n;
00216     portal_t *p;
00217     portal_t *portals[4096];
00218     
00219 // remove reference to the current portal
00220     pp = &l->portals;
00221     while (1)
00222     {
00223         t = *pp;
00224         if (!t)
00225             Error ("RemovePortalFromNode: portal not in leaf"); 
00226 
00227         if ( t == portal )
00228             break;
00229 
00230         if (t->nodes[0] == l)
00231             pp = &t->next[0];
00232         else if (t->nodes[1] == l)
00233             pp = &t->next[1];
00234         else
00235             Error ("RemovePortalFromNode: portal not bounding leaf");
00236     }
00237     
00238     if (portal->nodes[0] == l)
00239     {
00240         *pp = portal->next[0];
00241         portal->nodes[0] = NULL;
00242     } //end if
00243     else if (portal->nodes[1] == l)
00244     {
00245         *pp = portal->next[1];  
00246         portal->nodes[1] = NULL;
00247     } //end else if
00248     else
00249     {
00250         Error("RemovePortalFromNode: mislinked portal");
00251     } //end else
00252 //#ifdef ME
00253     n = 0;
00254     for (p = l->portals; p; p = p->next[s])
00255     {
00256         for (i = 0; i < n; i++)
00257         {
00258             if (p == portals[i]) Error("RemovePortalFromNode: circular linked\n");
00259         } //end for
00260         if (p->nodes[0] != l && p->nodes[1] != l)
00261         {
00262             Error("RemovePortalFromNodes: portal does not belong to node\n");
00263         } //end if
00264         portals[n] = p;
00265         s = (p->nodes[1] == l);
00266 //      if (++n >= 4096) Error("RemovePortalFromNode: more than 4096 portals\n");
00267     } //end for
00268 //#endif
00269 } //end of the function RemovePortalFromNode
00270 //===========================================================================
00271 //
00272 // Parameter:               -
00273 // Returns:                 -
00274 // Changes Globals:     -
00275 //===========================================================================
00276 void PrintPortal (portal_t *p)
00277 {
00278     int         i;
00279     winding_t   *w;
00280     
00281     w = p->winding;
00282     for (i=0 ; i<w->numpoints ; i++)
00283         printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
00284         , w->p[i][1], w->p[i][2]);
00285 } //end of the function PrintPortal
00286 //===========================================================================
00287 // The created portals will face the global outside_node
00288 //
00289 // Parameter:               -
00290 // Returns:                 -
00291 // Changes Globals:     -
00292 //===========================================================================
00293 #define SIDESPACE   8
00294 
00295 void MakeHeadnodePortals (tree_t *tree)
00296 {
00297     vec3_t      bounds[2];
00298     int         i, j, n;
00299     portal_t    *p, *portals[6];
00300     plane_t     bplanes[6], *pl;
00301     node_t *node;
00302 
00303     node = tree->headnode;
00304 
00305 // pad with some space so there will never be null volume leaves
00306     for (i=0 ; i<3 ; i++)
00307     {
00308         bounds[0][i] = tree->mins[i] - SIDESPACE;
00309         bounds[1][i] = tree->maxs[i] + SIDESPACE;
00310         if ( bounds[0][i] > bounds[1][i] ) {
00311             Error("empty BSP tree");
00312         }
00313     }
00314     
00315     tree->outside_node.planenum = PLANENUM_LEAF;
00316     tree->outside_node.brushlist = NULL;
00317     tree->outside_node.portals = NULL;
00318     tree->outside_node.contents = 0;
00319 
00320     for (i=0 ; i<3 ; i++)
00321         for (j=0 ; j<2 ; j++)
00322         {
00323             n = j*3 + i;
00324 
00325             p = AllocPortal ();
00326             portals[n] = p;
00327             
00328             pl = &bplanes[n];
00329             memset (pl, 0, sizeof(*pl));
00330             if (j)
00331             {
00332                 pl->normal[i] = -1;
00333                 pl->dist = -bounds[j][i];
00334             }
00335             else
00336             {
00337                 pl->normal[i] = 1;
00338                 pl->dist = bounds[j][i];
00339             }
00340             p->plane = *pl;
00341             p->winding = BaseWindingForPlane (pl->normal, pl->dist);
00342             AddPortalToNodes (p, node, &tree->outside_node);
00343         }
00344         
00345 // clip the basewindings by all the other planes
00346     for (i=0 ; i<6 ; i++)
00347     {
00348         for (j=0 ; j<6 ; j++)
00349         {
00350             if (j == i) continue;
00351             ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
00352         } //end for
00353     } //end for
00354 } //end of the function MakeHeadNodePortals
00355 //===========================================================================
00356 //
00357 // Parameter:               -
00358 // Returns:                 -
00359 // Changes Globals:     -
00360 //===========================================================================
00361 #define BASE_WINDING_EPSILON        0.001
00362 #define SPLIT_WINDING_EPSILON   0.001
00363 
00364 winding_t *BaseWindingForNode (node_t *node)
00365 {
00366     winding_t   *w;
00367     node_t      *n;
00368     plane_t     *plane;
00369     vec3_t      normal;
00370     vec_t       dist;
00371 
00372     w = BaseWindingForPlane (mapplanes[node->planenum].normal
00373         , mapplanes[node->planenum].dist);
00374 
00375     // clip by all the parents
00376     for (n=node->parent ; n && w ; )
00377     {
00378         plane = &mapplanes[n->planenum];
00379 
00380         if (n->children[0] == node)
00381         {   // take front
00382             ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
00383         }
00384         else
00385         {   // take back
00386             VectorSubtract (vec3_origin, plane->normal, normal);
00387             dist = -plane->dist;
00388             ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
00389         }
00390         node = n;
00391         n = n->parent;
00392     }
00393 
00394     return w;
00395 } //end of the function BaseWindingForNode
00396 //===========================================================================
00397 // create the new portal by taking the full plane winding for the cutting
00398 // plane and clipping it by all of parents of this node
00399 //
00400 // Parameter:               -
00401 // Returns:                 -
00402 // Changes Globals:     -
00403 //===========================================================================
00404 qboolean WindingIsTiny (winding_t *w);
00405 
00406 void MakeNodePortal (node_t *node)
00407 {
00408     portal_t    *new_portal, *p;
00409     winding_t   *w;
00410     vec3_t      normal;
00411     float       dist;
00412     int         side;
00413 
00414     w = BaseWindingForNode (node);
00415 
00416     // clip the portal by all the other portals in the node
00417     for (p = node->portals; p && w; p = p->next[side])  
00418     {
00419         if (p->nodes[0] == node)
00420         {
00421             side = 0;
00422             VectorCopy (p->plane.normal, normal);
00423             dist = p->plane.dist;
00424         } //end if
00425         else if (p->nodes[1] == node)
00426         {
00427             side = 1;
00428             VectorSubtract (vec3_origin, p->plane.normal, normal);
00429             dist = -p->plane.dist;
00430         } //end else if
00431         else
00432         {
00433             Error ("MakeNodePortal: mislinked portal");
00434         } //end else
00435         ChopWindingInPlace (&w, normal, dist, 0.1);
00436     } //end for
00437 
00438     if (!w)
00439     {
00440         return;
00441     } //end if
00442 
00443     if (WindingIsTiny (w))
00444     {
00445         c_tinyportals++;
00446         FreeWinding(w);
00447         return;
00448     } //end if
00449 
00450 #ifdef DEBUG
00451 /* //NOTE: don't use this winding ok check
00452     // all the invalid windings only have a degenerate edge
00453     if (WindingError(w))
00454     {
00455         Log_Print("MakeNodePortal: %s\n", WindingErrorString());
00456         FreeWinding(w);
00457         return;
00458     } //end if*/
00459 #endif //DEBUG
00460 
00461 
00462     new_portal = AllocPortal();
00463     new_portal->plane = mapplanes[node->planenum];
00464 
00465 #ifdef ME
00466     new_portal->planenum = node->planenum;
00467 #endif //ME
00468 
00469     new_portal->onnode = node;
00470     new_portal->winding = w;
00471     AddPortalToNodes (new_portal, node->children[0], node->children[1]);
00472 } //end of the function MakeNodePortal
00473 //===========================================================================
00474 // Move or split the portals that bound node so that the node's
00475 // children have portals instead of node.
00476 //
00477 // Parameter:               -
00478 // Returns:                 -
00479 // Changes Globals:     -
00480 //===========================================================================
00481 void SplitNodePortals (node_t *node)
00482 {
00483     portal_t    *p, *next_portal, *new_portal;
00484     node_t *f, *b, *other_node;
00485     int side;
00486     plane_t *plane;
00487     winding_t *frontwinding, *backwinding;
00488 
00489     plane = &mapplanes[node->planenum];
00490     f = node->children[0];
00491     b = node->children[1];
00492 
00493     for (p = node->portals ; p ; p = next_portal)   
00494     {
00495         if (p->nodes[0] == node) side = 0;
00496         else if (p->nodes[1] == node) side = 1;
00497         else Error ("SplitNodePortals: mislinked portal");
00498         next_portal = p->next[side];
00499 
00500         other_node = p->nodes[!side];
00501         RemovePortalFromNode (p, p->nodes[0]);
00502         RemovePortalFromNode (p, p->nodes[1]);
00503 
00504 //
00505 // cut the portal into two portals, one on each side of the cut plane
00506 //
00507         ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
00508                 SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
00509 
00510         if (frontwinding && WindingIsTiny(frontwinding))
00511         {
00512             FreeWinding (frontwinding);
00513             frontwinding = NULL;
00514             c_tinyportals++;
00515         } //end if
00516 
00517         if (backwinding && WindingIsTiny(backwinding))
00518         {
00519             FreeWinding (backwinding);
00520             backwinding = NULL;
00521             c_tinyportals++;
00522         } //end if
00523 
00524 #ifdef DEBUG
00525 /*  //NOTE: don't use this winding ok check
00526         // all the invalid windings only have a degenerate edge
00527         if (frontwinding && WindingError(frontwinding))
00528         {
00529             Log_Print("SplitNodePortals: front %s\n", WindingErrorString());
00530             FreeWinding(frontwinding);
00531             frontwinding = NULL;
00532         } //end if
00533         if (backwinding && WindingError(backwinding))
00534         {
00535             Log_Print("SplitNodePortals: back %s\n", WindingErrorString());
00536             FreeWinding(backwinding);
00537             backwinding = NULL;
00538         } //end if*/
00539 #endif //DEBUG
00540 
00541         if (!frontwinding && !backwinding)
00542         {   // tiny windings on both sides
00543             continue;
00544         }
00545 
00546         if (!frontwinding)
00547         {
00548             FreeWinding (backwinding);
00549             if (side == 0) AddPortalToNodes (p, b, other_node);
00550             else AddPortalToNodes (p, other_node, b);
00551             continue;
00552         }
00553         if (!backwinding)
00554         {
00555             FreeWinding (frontwinding);
00556             if (side == 0) AddPortalToNodes (p, f, other_node);
00557             else AddPortalToNodes (p, other_node, f);
00558             continue;
00559         }
00560         
00561     // the winding is split
00562         new_portal = AllocPortal();
00563         *new_portal = *p;
00564         new_portal->winding = backwinding;
00565         FreeWinding (p->winding);
00566         p->winding = frontwinding;
00567 
00568         if (side == 0)
00569         {
00570             AddPortalToNodes (p, f, other_node);
00571             AddPortalToNodes (new_portal, b, other_node);
00572         } //end if
00573         else
00574         {
00575             AddPortalToNodes (p, other_node, f);
00576             AddPortalToNodes (new_portal, other_node, b);
00577         } //end else
00578     }
00579 
00580     node->portals = NULL;
00581 } //end of the function SplitNodePortals
00582 //===========================================================================
00583 //
00584 // Parameter:               -
00585 // Returns:                 -
00586 // Changes Globals:     -
00587 //===========================================================================
00588 void CalcNodeBounds (node_t *node)
00589 {
00590     portal_t    *p;
00591     int         s;
00592     int         i;
00593 
00594     // calc mins/maxs for both leaves and nodes
00595     ClearBounds (node->mins, node->maxs);
00596     for (p = node->portals ; p ; p = p->next[s])    
00597     {
00598         s = (p->nodes[1] == node);
00599         for (i=0 ; i<p->winding->numpoints ; i++)
00600             AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
00601     }
00602 } //end of the function CalcNodeBounds
00603 //===========================================================================
00604 //
00605 // Parameter:               -
00606 // Returns:                 -
00607 // Changes Globals:     -
00608 //===========================================================================
00609 int c_numportalizednodes;
00610 
00611 void MakeTreePortals_r (node_t *node)
00612 {
00613     int     i;
00614 
00615 #ifdef ME
00616     qprintf("\r%6d", ++c_numportalizednodes);
00617     if (cancelconversion) return;
00618 #endif //ME
00619 
00620     CalcNodeBounds (node);
00621     if (node->mins[0] >= node->maxs[0])
00622     {
00623         Log_Print("WARNING: node without a volume\n");
00624     }
00625 
00626     for (i=0 ; i<3 ; i++)
00627     {
00628         if (node->mins[i] < -MAX_MAP_BOUNDS || node->maxs[i] > MAX_MAP_BOUNDS)
00629         {
00630             Log_Print("WARNING: node with unbounded volume\n");
00631             break;
00632         }
00633     }
00634     if (node->planenum == PLANENUM_LEAF)
00635         return;
00636 
00637     MakeNodePortal (node);
00638     SplitNodePortals (node);
00639 
00640     MakeTreePortals_r (node->children[0]);
00641     MakeTreePortals_r (node->children[1]);
00642 } //end of the function MakeTreePortals_r
00643 //===========================================================================
00644 //
00645 // Parameter:               -
00646 // Returns:                 -
00647 // Changes Globals:     -
00648 //===========================================================================
00649 void MakeTreePortals(tree_t *tree)
00650 {
00651 
00652 #ifdef ME
00653     Log_Print("---- Node Portalization ----\n");
00654     c_numportalizednodes = 0;
00655     c_portalmemory = 0;
00656     qprintf("%6d nodes portalized", c_numportalizednodes);
00657 #endif //ME
00658 
00659     MakeHeadnodePortals(tree);
00660     MakeTreePortals_r(tree->headnode);
00661 
00662 #ifdef ME
00663     qprintf("\n");
00664     Log_Write("%6d nodes portalized\r\n", c_numportalizednodes);
00665     Log_Print("%6d tiny portals\r\n", c_tinyportals);
00666     Log_Print("%6d KB of portal memory\r\n", c_portalmemory >> 10);
00667     Log_Print("%6i KB of winding memory\r\n", WindingMemory() >> 10);
00668 #endif //ME
00669 } //end of the function MakeTreePortals
00670 
00671 /*
00672 =========================================================
00673 
00674 FLOOD ENTITIES
00675 
00676 =========================================================
00677 */
00678 //#define P_NODESTACK
00679 
00680 node_t *p_firstnode;
00681 node_t *p_lastnode;
00682 
00683 //===========================================================================
00684 //
00685 // Parameter:               -
00686 // Returns:                 -
00687 // Changes Globals:     -
00688 //===========================================================================
00689 #ifdef P_NODESTACK
00690 void P_AddNodeToList(node_t *node)
00691 {
00692     node->next = p_firstnode;
00693     p_firstnode = node;
00694     if (!p_lastnode) p_lastnode = node;
00695 } //end of the function P_AddNodeToList
00696 #else //it's a node queue
00697 //add the node to the end of the node list
00698 void P_AddNodeToList(node_t *node)
00699 {
00700     node->next = NULL;
00701     if (p_lastnode) p_lastnode->next = node;
00702     else p_firstnode = node;
00703     p_lastnode = node;
00704 } //end of the function P_AddNodeToList
00705 #endif //P_NODESTACK
00706 //===========================================================================
00707 // get the first node from the front of the node list
00708 //
00709 // Parameter:               -
00710 // Returns:                 -
00711 // Changes Globals:     -
00712 //===========================================================================
00713 node_t *P_NextNodeFromList(void)
00714 {
00715     node_t *node;
00716 
00717     node = p_firstnode;
00718     if (p_firstnode) p_firstnode = p_firstnode->next;
00719     if (!p_firstnode) p_lastnode = NULL;
00720     return node;
00721 } //end of the function P_NextNodeFromList
00722 //===========================================================================
00723 //
00724 // Parameter:               -
00725 // Returns:                 -
00726 // Changes Globals:     -
00727 //===========================================================================
00728 void FloodPortals(node_t *firstnode)
00729 {
00730     node_t *node;
00731     portal_t *p;
00732     int s;
00733 
00734     firstnode->occupied = 1;
00735     P_AddNodeToList(firstnode);
00736 
00737     for (node = P_NextNodeFromList(); node; node = P_NextNodeFromList())
00738     {
00739         for (p = node->portals; p; p = p->next[s])
00740         {
00741             s = (p->nodes[1] == node);
00742             //if the node at the other side of the portal is occupied already
00743             if (p->nodes[!s]->occupied) continue;
00744             //if it isn't possible to flood through this portal
00745             if (!Portal_EntityFlood(p, s)) continue;
00746             //
00747             p->nodes[!s]->occupied = node->occupied + 1;
00748             //
00749             P_AddNodeToList(p->nodes[!s]);
00750         } //end for
00751     } //end for
00752 } //end of the function FloodPortals
00753 //===========================================================================
00754 //
00755 // Parameter:               -
00756 // Returns:                 -
00757 // Changes Globals:     -
00758 //===========================================================================
00759 int numrec;
00760 
00761 void FloodPortals_r (node_t *node, int dist)
00762 {
00763     portal_t *p;
00764     int s;
00765 //  int i;
00766 
00767     Log_Print("\r%6d", ++numrec);
00768 
00769     if (node->occupied) Error("FloodPortals_r: node already occupied\n");
00770     if (!node)
00771     {
00772         Error("FloodPortals_r: NULL node\n");
00773     } //end if*/
00774     node->occupied = dist;
00775 
00776     for (p = node->portals; p; p = p->next[s])
00777     {
00778         s = (p->nodes[1] == node);
00779         //if the node at the other side of the portal is occupied already
00780         if (p->nodes[!s]->occupied) continue;
00781         //if it isn't possible to flood through this portal
00782         if (!Portal_EntityFlood(p, s)) continue;
00783         //flood recursively through the current portal
00784         FloodPortals_r(p->nodes[!s], dist+1);
00785     } //end for
00786     Log_Print("\r%6d", --numrec);
00787 } //end of the function FloodPortals_r
00788 //===========================================================================
00789 //
00790 // Parameter:               -
00791 // Returns:                 -
00792 // Changes Globals:     -
00793 //===========================================================================
00794 qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
00795 {
00796     node_t *node;
00797     vec_t   d;
00798     plane_t *plane;
00799 
00800     //find the leaf to start in
00801     node = headnode;
00802     while(node->planenum != PLANENUM_LEAF)
00803     {
00804         if (node->planenum < 0 || node->planenum > nummapplanes)
00805         {
00806             Error("PlaceOccupant: invalid node->planenum\n");
00807         } //end if
00808         plane = &mapplanes[node->planenum];
00809         d = DotProduct(origin, plane->normal) - plane->dist;
00810         if (d >= 0) node = node->children[0];
00811         else node = node->children[1];
00812         if (!node)
00813         {
00814             Error("PlaceOccupant: invalid child %d\n", d < 0);
00815         } //end if
00816     } //end while
00817     //don't start in solid
00818 //  if (node->contents == CONTENTS_SOLID)
00819     //ME: replaced because in LeafNode in brushbsp.c
00820     //    some nodes have contents solid with other contents
00821     if (node->contents & CONTENTS_SOLID) return false;
00822     //if the node is already occupied
00823     if (node->occupied) return false;
00824 
00825     //place the occupant in the first leaf
00826     node->occupant = occupant;
00827 
00828     numrec = 0;
00829 //  Log_Print("%6d recurses", numrec);
00830 //  FloodPortals_r(node, 1);
00831 //  Log_Print("\n");
00832     FloodPortals(node);
00833 
00834     return true;
00835 } //end of the function PlaceOccupant
00836 //===========================================================================
00837 // Marks all nodes that can be reached by entites
00838 //
00839 // Parameter:               -
00840 // Returns:                 -
00841 // Changes Globals:     -
00842 //===========================================================================
00843 qboolean FloodEntities (tree_t *tree)
00844 {
00845     int i;
00846     int x, y;
00847     vec3_t origin;
00848     char *cl;
00849     qboolean inside;
00850     node_t *headnode;
00851 
00852     headnode = tree->headnode;
00853     Log_Print("------ FloodEntities -------\n");
00854     inside = false;
00855     tree->outside_node.occupied = 0;
00856 
00857     //start at entity 1 not the world ( = 0)
00858     for (i = 1; i < num_entities; i++)
00859     {
00860         GetVectorForKey(&entities[i], "origin", origin);
00861         if (VectorCompare(origin, vec3_origin)) continue;
00862 
00863         cl = ValueForKey(&entities[i], "classname");
00864         origin[2] += 1; //so objects on floor are ok
00865 
00866 //      Log_Print("flooding from entity %d: %s\n", i, cl);
00867         //nudge playerstart around if needed so clipping hulls allways
00868         //have a valid point
00869         if (!strcmp(cl, "info_player_start"))
00870         {
00871             for (x = -16; x <= 16; x += 16)
00872             {
00873                 for (y = -16; y <= 16; y += 16)
00874                 {
00875                     origin[0] += x;
00876                     origin[1] += y;
00877                     if (PlaceOccupant(headnode, origin, &entities[i]))
00878                     {
00879                         inside = true;
00880                         x = 999; //stop for this info_player_start
00881                         break;
00882                     } //end if
00883                     origin[0] -= x;
00884                     origin[1] -= y;
00885                 } //end for
00886             } //end for
00887         } //end if
00888         else
00889         {
00890             if (PlaceOccupant(headnode, origin, &entities[i]))
00891             {
00892                 inside = true;
00893             } //end if
00894         } //end else
00895     } //end for
00896 
00897     if (!inside)
00898     {
00899         Log_Print("WARNING: no entities inside\n");
00900     } //end if
00901     else if (tree->outside_node.occupied)
00902     {
00903         Log_Print("WARNING: entity reached from outside\n");
00904     } //end else if
00905 
00906     return (qboolean)(inside && !tree->outside_node.occupied);
00907 } //end of the function FloodEntities
00908 
00909 /*
00910 =========================================================
00911 
00912 FILL OUTSIDE
00913 
00914 =========================================================
00915 */
00916 
00917 int c_outside;
00918 int c_inside;
00919 int c_solid;
00920 
00921 //===========================================================================
00922 //
00923 // Parameter:               -
00924 // Returns:                 -
00925 // Changes Globals:     -
00926 //===========================================================================
00927 void FillOutside_r (node_t *node)
00928 {
00929     if (node->planenum != PLANENUM_LEAF)
00930     {
00931         FillOutside_r (node->children[0]);
00932         FillOutside_r (node->children[1]);
00933         return;
00934     } //end if
00935     // anything not reachable by an entity
00936     // can be filled away (by setting solid contents)
00937     if (!node->occupied)
00938     {
00939         if (!(node->contents & CONTENTS_SOLID))
00940         {
00941             c_outside++;
00942             node->contents |= CONTENTS_SOLID;
00943         } //end if
00944         else
00945         {
00946             c_solid++;
00947         } //end else
00948     } //end if
00949     else
00950     {
00951         c_inside++;
00952     } //end else
00953 } //end of the function FillOutside_r
00954 //===========================================================================
00955 // Fill all nodes that can't be reached by entities
00956 //
00957 // Parameter:               -
00958 // Returns:                 -
00959 // Changes Globals:     -
00960 //===========================================================================
00961 void FillOutside (node_t *headnode)
00962 {
00963     c_outside = 0;
00964     c_inside = 0;
00965     c_solid = 0;
00966     Log_Print("------- FillOutside --------\n");
00967     FillOutside_r (headnode);
00968     Log_Print("%5i solid leaves\n", c_solid);
00969     Log_Print("%5i leaves filled\n", c_outside);
00970     Log_Print("%5i inside leaves\n", c_inside);
00971 } //end of the function FillOutside
00972 
00973 /*
00974 =========================================================
00975 
00976 FLOOD AREAS
00977 
00978 =========================================================
00979 */
00980 
00981 int     c_areas;
00982 
00983 //===========================================================================
00984 //
00985 // Parameter:               -
00986 // Returns:                 -
00987 // Changes Globals:     -
00988 //===========================================================================
00989 void FloodAreas_r (node_t *node)
00990 {
00991     portal_t    *p;
00992     int         s;
00993     bspbrush_t  *b;
00994     entity_t    *e;
00995 
00996     if (node->contents == CONTENTS_AREAPORTAL)
00997     {
00998         // this node is part of an area portal
00999         b = node->brushlist;
01000         e = &entities[b->original->entitynum];
01001 
01002         // if the current area has allready touched this
01003         // portal, we are done
01004         if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
01005             return;
01006 
01007         // note the current area as bounding the portal
01008         if (e->portalareas[1])
01009         {
01010             Log_Print("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum);
01011             return;
01012         }
01013         if (e->portalareas[0])
01014             e->portalareas[1] = c_areas;
01015         else
01016             e->portalareas[0] = c_areas;
01017 
01018         return;
01019     } //end if
01020 
01021     if (node->area)
01022         return;     // allready got it
01023     node->area = c_areas;
01024 
01025     for (p=node->portals ; p ; p = p->next[s])
01026     {
01027         s = (p->nodes[1] == node);
01028 #if 0
01029         if (p->nodes[!s]->occupied)
01030             continue;
01031 #endif
01032         if (!Portal_EntityFlood (p, s))
01033             continue;
01034 
01035         FloodAreas_r (p->nodes[!s]);
01036     } //end for
01037 } //end of the function FloodAreas_r
01038 //===========================================================================
01039 // Just decend the tree, and for each node that hasn't had an
01040 // area set, flood fill out from there
01041 //
01042 // Parameter:               -
01043 // Returns:                 -
01044 // Changes Globals:     -
01045 //===========================================================================
01046 void FindAreas_r (node_t *node)
01047 {
01048     if (node->planenum != PLANENUM_LEAF)
01049     {
01050         FindAreas_r (node->children[0]);
01051         FindAreas_r (node->children[1]);
01052         return;
01053     }
01054 
01055     if (node->area)
01056         return;     // allready got it
01057 
01058     if (node->contents & CONTENTS_SOLID)
01059         return;
01060 
01061     if (!node->occupied)
01062         return;         // not reachable by entities
01063 
01064     // area portals are allways only flooded into, never
01065     // out of
01066     if (node->contents == CONTENTS_AREAPORTAL)
01067         return;
01068 
01069     c_areas++;
01070     FloodAreas_r (node);
01071 } //end of the function FindAreas_r
01072 //===========================================================================
01073 // Just decend the tree, and for each node that hasn't had an
01074 // area set, flood fill out from there
01075 //
01076 // Parameter:               -
01077 // Returns:                 -
01078 // Changes Globals:     -
01079 //===========================================================================
01080 void SetAreaPortalAreas_r (node_t *node)
01081 {
01082     bspbrush_t  *b;
01083     entity_t    *e;
01084 
01085     if (node->planenum != PLANENUM_LEAF)
01086     {
01087         SetAreaPortalAreas_r (node->children[0]);
01088         SetAreaPortalAreas_r (node->children[1]);
01089         return;
01090     } //end if
01091 
01092     if (node->contents == CONTENTS_AREAPORTAL)
01093     {
01094         if (node->area)
01095             return;     // allready set
01096 
01097         b = node->brushlist;
01098         e = &entities[b->original->entitynum];
01099         node->area = e->portalareas[0];
01100         if (!e->portalareas[1])
01101         {
01102             Log_Print("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum);
01103             return;
01104         } //end if
01105     } //end if
01106 } //end of the function SetAreaPortalAreas_r
01107 //===========================================================================
01108 //
01109 // Parameter:               -
01110 // Returns:                 -
01111 // Changes Globals:     -
01112 //===========================================================================
01113 /*
01114 void EmitAreaPortals(node_t *headnode)
01115 {
01116     int             i, j;
01117     entity_t        *e;
01118     dareaportal_t   *dp;
01119 
01120     if (c_areas > MAX_MAP_AREAS)
01121         Error ("MAX_MAP_AREAS");
01122     numareas = c_areas+1;
01123     numareaportals = 1;     // leave 0 as an error
01124 
01125     for (i=1 ; i<=c_areas ; i++)
01126     {
01127         dareas[i].firstareaportal = numareaportals;
01128         for (j=0 ; j<num_entities ; j++)
01129         {
01130             e = &entities[j];
01131             if (!e->areaportalnum)
01132                 continue;
01133             dp = &dareaportals[numareaportals];
01134             if (e->portalareas[0] == i)
01135             {
01136                 dp->portalnum = e->areaportalnum;
01137                 dp->otherarea = e->portalareas[1];
01138                 numareaportals++;
01139             } //end if
01140             else if (e->portalareas[1] == i)
01141             {
01142                 dp->portalnum = e->areaportalnum;
01143                 dp->otherarea = e->portalareas[0];
01144                 numareaportals++;
01145             } //end else if
01146         } //end for
01147         dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;
01148     } //end for
01149 
01150     Log_Print("%5i numareas\n", numareas);
01151     Log_Print("%5i numareaportals\n", numareaportals);
01152 } //end of the function EmitAreaPortals
01153 */
01154 //===========================================================================
01155 // Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
01156 //
01157 // Parameter:               -
01158 // Returns:                 -
01159 // Changes Globals:     -
01160 //===========================================================================
01161 void FloodAreas (tree_t *tree)
01162 {
01163     Log_Print("--- FloodAreas ---\n");
01164     FindAreas_r (tree->headnode);
01165     SetAreaPortalAreas_r (tree->headnode);
01166     Log_Print("%5i areas\n", c_areas);
01167 } //end of the function FloodAreas
01168 //===========================================================================
01169 // Finds a brush side to use for texturing the given portal
01170 //
01171 // Parameter:               -
01172 // Returns:                 -
01173 // Changes Globals:     -
01174 //===========================================================================
01175 void FindPortalSide (portal_t *p)
01176 {
01177     int         viscontents;
01178     bspbrush_t  *bb;
01179     mapbrush_t  *brush;
01180     node_t      *n;
01181     int         i,j;
01182     int         planenum;
01183     side_t      *side, *bestside;
01184     float       dot, bestdot;
01185     plane_t     *p1, *p2;
01186 
01187     // decide which content change is strongest
01188     // solid > lava > water, etc
01189     viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
01190     if (!viscontents)
01191         return;
01192 
01193     planenum = p->onnode->planenum;
01194     bestside = NULL;
01195     bestdot = 0;
01196 
01197     for (j=0 ; j<2 ; j++)
01198     {
01199         n = p->nodes[j];
01200         p1 = &mapplanes[p->onnode->planenum];
01201         for (bb=n->brushlist ; bb ; bb=bb->next)
01202         {
01203             brush = bb->original;
01204             if ( !(brush->contents & viscontents) )
01205                 continue;
01206             for (i=0 ; i<brush->numsides ; i++)
01207             {
01208                 side = &brush->original_sides[i];
01209                 if (side->flags & SFL_BEVEL)
01210                     continue;
01211                 if (side->texinfo == TEXINFO_NODE)
01212                     continue;       // non-visible
01213                 if ((side->planenum&~1) == planenum)
01214                 {   // exact match
01215                     bestside = &brush->original_sides[i];
01216                     goto gotit;
01217                 } //end if
01218                 // see how close the match is
01219                 p2 = &mapplanes[side->planenum&~1];
01220                 dot = DotProduct (p1->normal, p2->normal);
01221                 if (dot > bestdot)
01222                 {
01223                     bestdot = dot;
01224                     bestside = side;
01225                 } //end if
01226             } //end for
01227         } //end for
01228     } //end for
01229 
01230 gotit:
01231     if (!bestside)
01232         Log_Print("WARNING: side not found for portal\n");
01233 
01234     p->sidefound = true;
01235     p->side = bestside;
01236 } //end of the function FindPortalSide
01237 //===========================================================================
01238 //
01239 // Parameter:               -
01240 // Returns:                 -
01241 // Changes Globals:     -
01242 //===========================================================================
01243 void MarkVisibleSides_r (node_t *node)
01244 {
01245     portal_t *p;
01246     int s;
01247 
01248     if (node->planenum != PLANENUM_LEAF)
01249     {
01250         MarkVisibleSides_r (node->children[0]);
01251         MarkVisibleSides_r (node->children[1]);
01252         return;
01253     } //end if
01254 
01255     // empty leaves are never boundary leaves
01256     if (!node->contents) return;
01257 
01258     // see if there is a visible face
01259     for (p=node->portals ; p ; p = p->next[!s])
01260     {
01261         s = (p->nodes[0] == node);
01262         if (!p->onnode)
01263             continue;       // edge of world
01264         if (!p->sidefound)
01265             FindPortalSide (p);
01266         if (p->side)
01267             p->side->flags |= SFL_VISIBLE;
01268     } //end for
01269 } //end of the function MarkVisibleSides_r
01270 //===========================================================================
01271 //
01272 // Parameter:               -
01273 // Returns:                 -
01274 // Changes Globals:     -
01275 //===========================================================================
01276 void MarkVisibleSides(tree_t *tree, int startbrush, int endbrush)
01277 {
01278     int     i, j;
01279     mapbrush_t  *mb;
01280     int     numsides;
01281 
01282     Log_Print("--- MarkVisibleSides ---\n");
01283 
01284     // clear all the visible flags
01285     for (i=startbrush ; i<endbrush ; i++)
01286     {
01287         mb = &mapbrushes[i];
01288 
01289         numsides = mb->numsides;
01290         for (j=0 ; j<numsides ; j++)
01291             mb->original_sides[j].flags &= ~SFL_VISIBLE;
01292     }
01293 
01294     // set visible flags on the sides that are used by portals
01295     MarkVisibleSides_r (tree->headnode);
01296 } //end of the function MarkVisibleSides
01297 

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