Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

portals.c (16497B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 #include "qbsp.h"
     24 
     25 
     26 int		c_active_portals;
     27 int		c_peak_portals;
     28 int		c_boundary;
     29 int		c_boundary_sides;
     30 
     31 /*
     32 ===========
     33 AllocPortal
     34 ===========
     35 */
     36 portal_t *AllocPortal (void)
     37 {
     38 	portal_t	*p;
     39 	
     40 	if (numthreads == 1)
     41 		c_active_portals++;
     42 	if (c_active_portals > c_peak_portals)
     43 		c_peak_portals = c_active_portals;
     44 	
     45 	p = malloc (sizeof(portal_t));
     46 	memset (p, 0, sizeof(portal_t));
     47 	
     48 	return p;
     49 }
     50 
     51 void FreePortal (portal_t *p)
     52 {
     53 	if (p->winding)
     54 		FreeWinding (p->winding);
     55 	if (numthreads == 1)
     56 		c_active_portals--;
     57 	free (p);
     58 }
     59 
     60 //==============================================================
     61 
     62 /*
     63 =============
     64 Portal_Passable
     65 
     66 Returns true if the portal has non-opaque leafs on both sides
     67 =============
     68 */
     69 qboolean Portal_Passable(portal_t *p) {
     70 	if (!p->onnode) {
     71 		return qfalse;	// to global outsideleaf
     72 	}
     73 
     74 	if (p->nodes[0]->planenum != PLANENUM_LEAF
     75 		|| p->nodes[1]->planenum != PLANENUM_LEAF) {
     76 		Error ("Portal_EntityFlood: not a leaf");
     77 	}
     78 
     79 	if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
     80 		return qtrue;
     81 	}
     82 
     83 	return qfalse;
     84 }
     85 
     86 
     87 //=============================================================================
     88 
     89 int		c_tinyportals;
     90 
     91 /*
     92 =============
     93 AddPortalToNodes
     94 =============
     95 */
     96 void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
     97 {
     98 	if (p->nodes[0] || p->nodes[1])
     99 		Error ("AddPortalToNode: allready included");
    100 
    101 	p->nodes[0] = front;
    102 	p->next[0] = front->portals;
    103 	front->portals = p;
    104 	
    105 	p->nodes[1] = back;
    106 	p->next[1] = back->portals;
    107 	back->portals = p;
    108 }
    109 
    110 
    111 /*
    112 =============
    113 RemovePortalFromNode
    114 =============
    115 */
    116 void RemovePortalFromNode (portal_t *portal, node_t *l)
    117 {
    118 	portal_t	**pp, *t;
    119 	
    120 // remove reference to the current portal
    121 	pp = &l->portals;
    122 	while (1)
    123 	{
    124 		t = *pp;
    125 		if (!t)
    126 			Error ("RemovePortalFromNode: portal not in leaf");	
    127 
    128 		if ( t == portal )
    129 			break;
    130 
    131 		if (t->nodes[0] == l)
    132 			pp = &t->next[0];
    133 		else if (t->nodes[1] == l)
    134 			pp = &t->next[1];
    135 		else
    136 			Error ("RemovePortalFromNode: portal not bounding leaf");
    137 	}
    138 	
    139 	if (portal->nodes[0] == l)
    140 	{
    141 		*pp = portal->next[0];
    142 		portal->nodes[0] = NULL;
    143 	}
    144 	else if (portal->nodes[1] == l)
    145 	{
    146 		*pp = portal->next[1];	
    147 		portal->nodes[1] = NULL;
    148 	}
    149 }
    150 
    151 //============================================================================
    152 
    153 void PrintPortal (portal_t *p)
    154 {
    155 	int			i;
    156 	winding_t	*w;
    157 	
    158 	w = p->winding;
    159 	for (i=0 ; i<w->numpoints ; i++)
    160 		_printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
    161 		, w->p[i][1], w->p[i][2]);
    162 }
    163 
    164 /*
    165 ================
    166 MakeHeadnodePortals
    167 
    168 The created portals will face the global outside_node
    169 ================
    170 */
    171 #define	SIDESPACE	8
    172 void MakeHeadnodePortals (tree_t *tree)
    173 {
    174 	vec3_t		bounds[2];
    175 	int			i, j, n;
    176 	portal_t	*p, *portals[6];
    177 	plane_t		bplanes[6], *pl;
    178 	node_t *node;
    179 
    180 	node = tree->headnode;
    181 
    182 // pad with some space so there will never be null volume leafs
    183 	for (i=0 ; i<3 ; i++)
    184 	{
    185 		bounds[0][i] = tree->mins[i] - SIDESPACE;
    186 		bounds[1][i] = tree->maxs[i] + SIDESPACE;
    187 		if ( bounds[0][i] >= bounds[1][i] ) {
    188 			Error( "Backwards tree volume" );
    189 		}
    190 	}
    191 	
    192 	tree->outside_node.planenum = PLANENUM_LEAF;
    193 	tree->outside_node.brushlist = NULL;
    194 	tree->outside_node.portals = NULL;
    195 	tree->outside_node.opaque = qfalse;
    196 
    197 	for (i=0 ; i<3 ; i++)
    198 		for (j=0 ; j<2 ; j++)
    199 		{
    200 			n = j*3 + i;
    201 
    202 			p = AllocPortal ();
    203 			portals[n] = p;
    204 			
    205 			pl = &bplanes[n];
    206 			memset (pl, 0, sizeof(*pl));
    207 			if (j)
    208 			{
    209 				pl->normal[i] = -1;
    210 				pl->dist = -bounds[j][i];
    211 			}
    212 			else
    213 			{
    214 				pl->normal[i] = 1;
    215 				pl->dist = bounds[j][i];
    216 			}
    217 			p->plane = *pl;
    218 			p->winding = BaseWindingForPlane (pl->normal, pl->dist);
    219 			AddPortalToNodes (p, node, &tree->outside_node);
    220 		}
    221 		
    222 // clip the basewindings by all the other planes
    223 	for (i=0 ; i<6 ; i++)
    224 	{
    225 		for (j=0 ; j<6 ; j++)
    226 		{
    227 			if (j == i)
    228 				continue;
    229 			ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
    230 		}
    231 	}
    232 }
    233 
    234 //===================================================
    235 
    236 
    237 /*
    238 ================
    239 BaseWindingForNode
    240 ================
    241 */
    242 #define	BASE_WINDING_EPSILON	0.001
    243 #define	SPLIT_WINDING_EPSILON	0.001
    244 
    245 winding_t	*BaseWindingForNode (node_t *node)
    246 {
    247 	winding_t	*w;
    248 	node_t		*n;
    249 	plane_t		*plane;
    250 	vec3_t		normal;
    251 	vec_t		dist;
    252 
    253 	w = BaseWindingForPlane (mapplanes[node->planenum].normal
    254 		, mapplanes[node->planenum].dist);
    255 
    256 	// clip by all the parents
    257 	for (n=node->parent ; n && w ; )
    258 	{
    259 		plane = &mapplanes[n->planenum];
    260 
    261 		if (n->children[0] == node)
    262 		{	// take front
    263 			ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
    264 		}
    265 		else
    266 		{	// take back
    267 			VectorSubtract (vec3_origin, plane->normal, normal);
    268 			dist = -plane->dist;
    269 			ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
    270 		}
    271 		node = n;
    272 		n = n->parent;
    273 	}
    274 
    275 	return w;
    276 }
    277 
    278 //============================================================
    279 
    280 /*
    281 ==================
    282 MakeNodePortal
    283 
    284 create the new portal by taking the full plane winding for the cutting plane
    285 and clipping it by all of parents of this node
    286 ==================
    287 */
    288 void MakeNodePortal (node_t *node)
    289 {
    290 	portal_t	*new_portal, *p;
    291 	winding_t	*w;
    292 	vec3_t		normal;
    293 	float		dist;
    294 	int			side;
    295 
    296 	w = BaseWindingForNode (node);
    297 
    298 	// clip the portal by all the other portals in the node
    299 	for (p = node->portals ; p && w; p = p->next[side])	
    300 	{
    301 		if (p->nodes[0] == node)
    302 		{
    303 			side = 0;
    304 			VectorCopy (p->plane.normal, normal);
    305 			dist = p->plane.dist;
    306 		}
    307 		else if (p->nodes[1] == node)
    308 		{
    309 			side = 1;
    310 			VectorSubtract (vec3_origin, p->plane.normal, normal);
    311 			dist = -p->plane.dist;
    312 		}
    313 		else
    314 			Error ("CutNodePortals_r: mislinked portal");
    315 
    316 		ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON);
    317 	}
    318 
    319 	if (!w)
    320 	{
    321 		return;
    322 	}
    323 
    324 	if (WindingIsTiny (w))
    325 	{
    326 		c_tinyportals++;
    327 		FreeWinding (w);
    328 		return;
    329 	}
    330 
    331 	new_portal = AllocPortal ();
    332 	new_portal->plane = mapplanes[node->planenum];
    333 	new_portal->onnode = node;
    334 	new_portal->winding = w;
    335 	new_portal->hint = node->hint;
    336 	AddPortalToNodes (new_portal, node->children[0], node->children[1]);
    337 }
    338 
    339 
    340 /*
    341 ==============
    342 SplitNodePortals
    343 
    344 Move or split the portals that bound node so that the node's
    345 children have portals instead of node.
    346 ==============
    347 */
    348 void SplitNodePortals (node_t *node)
    349 {
    350 	portal_t	*p, *next_portal, *new_portal;
    351 	node_t		*f, *b, *other_node;
    352 	int			side;
    353 	plane_t		*plane;
    354 	winding_t	*frontwinding, *backwinding;
    355 
    356 	plane = &mapplanes[node->planenum];
    357 	f = node->children[0];
    358 	b = node->children[1];
    359 
    360 	for (p = node->portals ; p ; p = next_portal)	
    361 	{
    362 		if (p->nodes[0] == node)
    363 			side = 0;
    364 		else if (p->nodes[1] == node)
    365 			side = 1;
    366 		else
    367 			Error ("SplitNodePortals: mislinked portal");
    368 		next_portal = p->next[side];
    369 
    370 		other_node = p->nodes[!side];
    371 		RemovePortalFromNode (p, p->nodes[0]);
    372 		RemovePortalFromNode (p, p->nodes[1]);
    373 
    374 //
    375 // cut the portal into two portals, one on each side of the cut plane
    376 //
    377 		ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
    378 			SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
    379 
    380 		if (frontwinding && WindingIsTiny(frontwinding))
    381 		{
    382 			if (!f->tinyportals)
    383 				VectorCopy(frontwinding->p[0], f->referencepoint);
    384 			f->tinyportals++;
    385 			if (!other_node->tinyportals)
    386 				VectorCopy(frontwinding->p[0], other_node->referencepoint);
    387 			other_node->tinyportals++;
    388 
    389 			FreeWinding (frontwinding);
    390 			frontwinding = NULL;
    391 			c_tinyportals++;
    392 		}
    393 
    394 		if (backwinding && WindingIsTiny(backwinding))
    395 		{
    396 			if (!b->tinyportals)
    397 				VectorCopy(backwinding->p[0], b->referencepoint);
    398 			b->tinyportals++;
    399 			if (!other_node->tinyportals)
    400 				VectorCopy(backwinding->p[0], other_node->referencepoint);
    401 			other_node->tinyportals++;
    402 
    403 			FreeWinding (backwinding);
    404 			backwinding = NULL;
    405 			c_tinyportals++;
    406 		}
    407 
    408 		if (!frontwinding && !backwinding)
    409 		{	// tiny windings on both sides
    410 			continue;
    411 		}
    412 
    413 		if (!frontwinding)
    414 		{
    415 			FreeWinding (backwinding);
    416 			if (side == 0)
    417 				AddPortalToNodes (p, b, other_node);
    418 			else
    419 				AddPortalToNodes (p, other_node, b);
    420 			continue;
    421 		}
    422 		if (!backwinding)
    423 		{
    424 			FreeWinding (frontwinding);
    425 			if (side == 0)
    426 				AddPortalToNodes (p, f, other_node);
    427 			else
    428 				AddPortalToNodes (p, other_node, f);
    429 			continue;
    430 		}
    431 		
    432 	// the winding is split
    433 		new_portal = AllocPortal ();
    434 		*new_portal = *p;
    435 		new_portal->winding = backwinding;
    436 		FreeWinding (p->winding);
    437 		p->winding = frontwinding;
    438 
    439 		if (side == 0)
    440 		{
    441 			AddPortalToNodes (p, f, other_node);
    442 			AddPortalToNodes (new_portal, b, other_node);
    443 		}
    444 		else
    445 		{
    446 			AddPortalToNodes (p, other_node, f);
    447 			AddPortalToNodes (new_portal, other_node, b);
    448 		}
    449 	}
    450 
    451 	node->portals = NULL;
    452 }
    453 
    454 
    455 /*
    456 ================
    457 CalcNodeBounds
    458 ================
    459 */
    460 void CalcNodeBounds (node_t *node)
    461 {
    462 	portal_t	*p;
    463 	int			s;
    464 	int			i;
    465 
    466 	// calc mins/maxs for both leafs and nodes
    467 	ClearBounds (node->mins, node->maxs);
    468 	for (p = node->portals ; p ; p = p->next[s])
    469 	{
    470 		s = (p->nodes[1] == node);
    471 		for (i=0 ; i<p->winding->numpoints ; i++)
    472 			AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
    473 	}
    474 }
    475 
    476 /*
    477 ==================
    478 MakeTreePortals_r
    479 ==================
    480 */
    481 void MakeTreePortals_r (node_t *node)
    482 {
    483 	int		i;
    484 
    485 	CalcNodeBounds (node);
    486 	if (node->mins[0] >= node->maxs[0])
    487 	{
    488 		_printf ("WARNING: node without a volume\n");
    489 		_printf("node has %d tiny portals\n", node->tinyportals);
    490 		_printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0],
    491 															node->referencepoint[1],
    492 															node->referencepoint[2]);
    493 	}
    494 
    495 	for (i=0 ; i<3 ; i++)
    496 	{
    497 		if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD)
    498 		{
    499 			_printf ("WARNING: node with unbounded volume\n");
    500 			break;
    501 		}
    502 	}
    503 	if (node->planenum == PLANENUM_LEAF)
    504 		return;
    505 
    506 	MakeNodePortal (node);
    507 	SplitNodePortals (node);
    508 
    509 	MakeTreePortals_r (node->children[0]);
    510 	MakeTreePortals_r (node->children[1]);
    511 }
    512 
    513 /*
    514 ==================
    515 MakeTreePortals
    516 ==================
    517 */
    518 void MakeTreePortals (tree_t *tree)
    519 {
    520 	qprintf( "----- MakeTreePortals -----\n");
    521 	MakeHeadnodePortals (tree);
    522 	MakeTreePortals_r (tree->headnode);
    523 	qprintf("%6d tiny portals\n", c_tinyportals);
    524 }
    525 
    526 /*
    527 =========================================================
    528 
    529 FLOOD ENTITIES
    530 
    531 =========================================================
    532 */
    533 
    534 int		c_floodedleafs;
    535 
    536 /*
    537 =============
    538 FloodPortals_r
    539 =============
    540 */
    541 void FloodPortals_r (node_t *node, int dist) {
    542 	portal_t	*p;
    543 	int			s;
    544 
    545 	if ( node->occupied ) {
    546 		return;
    547 	}
    548 
    549 	if ( node->opaque ) {
    550 		return;
    551 	}
    552 
    553 	c_floodedleafs++;
    554 	node->occupied = dist;
    555 
    556 	for (p=node->portals ; p ; p = p->next[s]) {
    557 		s = (p->nodes[1] == node);
    558 		FloodPortals_r (p->nodes[!s], dist+1);
    559 	}
    560 }
    561 
    562 /*
    563 =============
    564 PlaceOccupant
    565 =============
    566 */
    567 qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
    568 {
    569 	node_t	*node;
    570 	vec_t	d;
    571 	plane_t	*plane;
    572 
    573 	// find the leaf to start in
    574 	node = headnode;
    575 	while (node->planenum != PLANENUM_LEAF)
    576 	{
    577 		plane = &mapplanes[node->planenum];
    578 		d = DotProduct (origin, plane->normal) - plane->dist;
    579 		if (d >= 0)
    580 			node = node->children[0];
    581 		else
    582 			node = node->children[1];
    583 	}
    584 
    585 	if ( node->opaque )
    586 		return qfalse;
    587 	node->occupant = occupant;
    588 
    589 	FloodPortals_r (node, 1);
    590 
    591 	return qtrue;
    592 }
    593 
    594 /*
    595 =============
    596 FloodEntities
    597 
    598 Marks all nodes that can be reached by entites
    599 =============
    600 */
    601 qboolean FloodEntities( tree_t *tree ) {
    602 	int		i;
    603 	vec3_t	origin;
    604 	const char	*cl;
    605 	qboolean	inside;
    606 	node_t *headnode;
    607 
    608 	headnode = tree->headnode;
    609 	qprintf ("--- FloodEntities ---\n");
    610 	inside = qfalse;
    611 	tree->outside_node.occupied = 0;
    612 
    613 	c_floodedleafs = 0;
    614 	for (i=1 ; i<num_entities ; i++)
    615 	{
    616 		GetVectorForKey (&entities[i], "origin", origin);
    617 		if (VectorCompare(origin, vec3_origin))
    618 			continue;
    619 
    620 		cl = ValueForKey (&entities[i], "classname");
    621 
    622 		origin[2] += 1;	// so objects on floor are ok
    623 
    624 		if (PlaceOccupant (headnode, origin, &entities[i]))
    625 			inside = qtrue;
    626 	}
    627 
    628 	qprintf("%5i flooded leafs\n", c_floodedleafs );
    629 
    630 	if (!inside)
    631 	{
    632 		qprintf ("no entities in open -- no filling\n");
    633 	}
    634 	else if (tree->outside_node.occupied)
    635 	{
    636 		qprintf ("entity reached from outside -- no filling\n");
    637 	}
    638 
    639 	return (qboolean)(inside && !tree->outside_node.occupied);
    640 }
    641 
    642 /*
    643 =========================================================
    644 
    645 FLOOD AREAS
    646 
    647 =========================================================
    648 */
    649 
    650 int		c_areas;
    651 
    652 /*
    653 =============
    654 FloodAreas_r
    655 =============
    656 */
    657 void FloodAreas_r (node_t *node)
    658 {
    659 	portal_t	*p;
    660 	int			s;
    661 	bspbrush_t	*b;
    662 
    663 	if ( node->areaportal ) {
    664 		//
    665 		if ( node->area == -1 ) {
    666 			node->area = c_areas;
    667 		}
    668 		// this node is part of an area portal brush
    669 		b = node->brushlist->original;
    670 
    671 		// if the current area has allready touched this
    672 		// portal, we are done
    673 		if (b->portalareas[0] == c_areas || b->portalareas[1] == c_areas)
    674 			return;
    675 
    676 		// note the current area as bounding the portal
    677 		if (b->portalareas[1] != -1)
    678 		{
    679 			_printf ("WARNING: areaportal brush %i touches > 2 areas\n", b->brushnum );
    680 			return;
    681 		}
    682 		if (b->portalareas[0] != -1) {
    683 			b->portalareas[1] = c_areas;
    684 		} else {
    685 			b->portalareas[0] = c_areas;
    686 		}
    687 
    688 		return;
    689 	}
    690 
    691 	if (node->area != -1) {
    692 		return;		// allready got it
    693 	}
    694 	if ( node->cluster == -1 ) {
    695 		return;
    696 	}
    697 
    698 	node->area = c_areas;
    699 
    700 	for (p=node->portals ; p ; p = p->next[s])
    701 	{
    702 		s = (p->nodes[1] == node);
    703 
    704 		if ( !Portal_Passable(p) )
    705 			continue;
    706 
    707 		FloodAreas_r (p->nodes[!s]);
    708 	}
    709 }
    710 
    711 
    712 /*
    713 =============
    714 FindAreas_r
    715 
    716 Just decend the tree, and for each node that hasn't had an
    717 area set, flood fill out from there
    718 =============
    719 */
    720 void FindAreas_r (node_t *node)
    721 {
    722 	if (node->planenum != PLANENUM_LEAF)
    723 	{
    724 		FindAreas_r (node->children[0]);
    725 		FindAreas_r (node->children[1]);
    726 		return;
    727 	}
    728 
    729 	if (node->opaque)
    730 		return;
    731 
    732 	if (node->areaportal)
    733 		return;
    734 
    735 	if (node->area != -1)
    736 		return;		// allready got it
    737 
    738 	FloodAreas_r (node);
    739 	c_areas++;
    740 }
    741 
    742 /*
    743 =============
    744 CheckAreas_r
    745 =============
    746 */
    747 void CheckAreas_r (node_t *node)
    748 {
    749 	bspbrush_t	*b;
    750 
    751 	if (node->planenum != PLANENUM_LEAF)
    752 	{
    753 		CheckAreas_r (node->children[0]);
    754 		CheckAreas_r (node->children[1]);
    755 		return;
    756 	}
    757 
    758 	if (node->opaque)
    759 		return;
    760 
    761 	if (node->cluster != -1)
    762 		if (node->area == -1)
    763 			_printf("WARNING: cluster %d has area set to -1\n", node->cluster);
    764 	if (node->areaportal)
    765 	{
    766 		b = node->brushlist->original;
    767 
    768 		// check if the areaportal touches two areas
    769 		if (b->portalareas[0] == -1 || b->portalareas[1] == -1)
    770 			_printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushnum);
    771 	}
    772 }
    773 
    774 /*
    775 =============
    776 FloodAreas
    777 
    778 Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
    779 =============
    780 */
    781 void FloodAreas (tree_t *tree)
    782 {
    783 	qprintf ("--- FloodAreas ---\n");
    784 	FindAreas_r( tree->headnode );
    785 
    786 	// check for areaportal brushes that don't touch two areas
    787 	CheckAreas_r( tree->headnode );
    788 
    789 	qprintf ("%5i areas\n", c_areas);
    790 }
    791 
    792 //======================================================
    793 
    794 int		c_outside;
    795 int		c_inside;
    796 int		c_solid;
    797 
    798 void FillOutside_r (node_t *node)
    799 {
    800 	if (node->planenum != PLANENUM_LEAF)
    801 	{
    802 		FillOutside_r (node->children[0]);
    803 		FillOutside_r (node->children[1]);
    804 		return;
    805 	}
    806 
    807 	// anything not reachable by an entity
    808 	// can be filled away
    809 	if (!node->occupied) {
    810 		if ( !node->opaque ) {
    811 			c_outside++;
    812 			node->opaque = qtrue;
    813 		} else {
    814 			c_solid++;
    815 		}
    816 	} else {
    817 		c_inside++;
    818 	}
    819 
    820 }
    821 
    822 /*
    823 =============
    824 FillOutside
    825 
    826 Fill all nodes that can't be reached by entities
    827 =============
    828 */
    829 void FillOutside (node_t *headnode)
    830 {
    831 	c_outside = 0;
    832 	c_inside = 0;
    833 	c_solid = 0;
    834 	qprintf ("--- FillOutside ---\n");
    835 	FillOutside_r (headnode);
    836 	qprintf ("%5i solid leafs\n", c_solid);
    837 	qprintf ("%5i leafs filled\n", c_outside);
    838 	qprintf ("%5i inside leafs\n", c_inside);
    839 }
    840 
    841 
    842 //==============================================================
    843